迈向更好的命名

Avatar of Cory Long
Cory Long

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

有一句名言,我敢打赌你们每个人都听过很多次了。你们知道的。 提醒你命名有多难的那句。

让我们来谈谈命名。

我们经常谈论命名有多难,但很少看到人们谈论如何变得更擅长命名。即使是命名哲学也为命名提供了结构,但并不会为你选择名称。名称不仅仅是我们不必要地陷入其中的一些难题。它们对良好的代码至关重要。好的名称可以让代码库易于上手和使用。坏的名称难以理解,或者更糟糕的是,容易出错。

命名示例

让我们来看一些 JavaScript 中的示例。

function processData(data) {
  var result = JSON.parse(data);
  return result;
}

仅阅读函数名、参数名和返回变量,我们看到 processData 获取 data 并返回 result。这些名称几乎没有向读者提供任何信息。当您只想让代码工作或试图保持对更改的灵活时,编写这样的代码很容易,这也没问题。始终建议用新的眼光检查代码以修复问题,并且名称应包含在您的质量检查清单中。

这是一个更具描述性的方法,我们可以用它来编写上一个示例

function parseJson(string) {
  var jsonObject = JSON.parse(string);
  return jsonObject;
}

技术是使用缩写和首字母缩略词最多的领域之一,它们是优秀名称的关键。FTP 比“文件传输协议”更容易阅读和理解。但在某些情况下,读者可能会被排除在外。

这是一个示例,其中缩写对于编写代码的开发人员来说很方便,但对于需要阅读或参与其中的任何其他人来说却不太方便

function cts(con, ack) {
  if (con && ack) {
    return true;
  }
  else {
    return false;
  }
}

我经常会阅读包含首字母缩略词的代码,然后不得不切换到我的网络浏览器进行搜索,却只找到汽车型号的结果。一个完美的例子是 cts,它会返回很多凯迪拉克的结果。ack 在搜索中确实会出现,但我宁愿不把它放在我的文本编辑器中。con 可以被误解成多种含义。它是连接吗?计数?容器?也许这是一个骗局。如果您熟悉代码库,这些东西可能很明显,但它会增加加入项目的学习曲线。几秒钟的额外时间可以节省读者多年来的几分钟时间。

以下是前面示例中没有使用缩写的版本

function clearToSend(connectionExists, acknowledgementStatus) {
  if (connectionExists && acknowledgementStatus) {
    return true;
  }
  else {
    return false;
  }
}

让我们转向一些 HTML 示例,因为 HTML 可能是所有语言中名称最多的语言。

<section class="new-promotion-parent">
  <img class="logo" src="small-square-logo-monochrome.png"/>
  <div class="get-your-coupon">
    <p>Get Your Coupon</p>
  </div>
</section>

我们可以想象,这里使用“new”这个词可能是因为设计师被告知要更新 promotion-parent 元素,但也要保持对现有类的支持,也许是为了保留旧的 HTML。术语“new”在最初的几个月内是准确的描述,但随着时间的推移,它变得越来越具有讽刺意味。更好的方法可能是添加一个单独的类来描述什么是新的。例如,如果正在实施新的扁平化设计,则可以使用 flat-design 作为类名。作为额外的好处,如果您想重用一些样式,可以使来自现有 promotion-parent 类的 CSS 沿用下来。

类似地,logo 最初看起来像一个合理的类名。然而,不可避免地,第二个 logo 会出现在某个地方,它被命名为 alt-logo。logo 不断出现,名字也越来越糟糕。大多数资源都有几种变体,例如不同的形状、大小、颜色方案等等。也就是说,small-square-logo-monochrome 也不算是一个好的类名,因为图像本身应该能够在不使类名过时的情况下进行交换。更好的方法可能是使用一个描述资源作用而不是类型或外观的名称。

在这里,div 的语言被用来命名 div 为 get-your-coupon。HTML 文档的内容旨在不断发展;名称则不然。今天的优惠券代码将来可能成为电子邮件注册,同时保持相同的样式和功能。HTML 是名称经常过于具体而不是过于模糊的地方之一。

以下是考虑了建议后的相同 HTML 代码

<section class="flat-design promotion-parent">
  <img class="promotion-branding-image" src="small-square-logo-monochrome.png"/>
  <div class="primary-promotion-text">
    <p>Get Your Coupon</p>
  </div>
</section>

我们甚至可以查看数据库中的名称以获得更好的命名。表通常在应用程序中跨多个非常不同的上下文中使用无数次。

这是一个简化的数据库表

CREATE TABLE `book` (
  `id` int(12) NOT NULL,
  `title` varchar(50) NOT NULL,
  `author` varchar(50) NOT NULL,
  `type` bit(1) NOT NULL,
  `sort` int(12) NOT NULL,
  `order` varchar(25) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在书籍的上下文中,type 代表什么?是指小说还是非小说?是指平装本还是精装本?也许是指实体书还是电子书?

sort 列同样令人困惑。它是表示 ASC 还是 DESC?它表示用于排序的列?也许它决定排序是否处于活动状态?或者它决定以其他替代顺序显示书籍?

然后是 order。除了同样模棱两可之外,order 还是 MySQL 和许多其他语言中的保留字。您可以使用反引号 (`) 来解决 MySQL 中的此问题,但最好完全避免使用保留字。

以下是如何以更具描述性的方式编写该表

CREATE TABLE `book` (
  `id` int(12) NOT NULL,
  `title` varchar(50) NOT NULL,
  `author` varchar(50) NOT NULL,
  `cover_type` bit(1) NOT NULL,
  `sort_order` int(12) NOT NULL,
  `purchase_order_number` varchar(25) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

命名约定

让我们来谈谈命名约定。

if (oldmanshaven) {
  return true;
}

您是将其读作 Old Mans Haven 还是 Old Man Shaven?无论哪种方式,它都会迫使您放慢速度并思考,这会累加起来,并可能有一天导致误解。PascalCase、camelCase、snake_case、kebab-case 都是极好的选择。使用它们,同样重要的是,要保持一致。

以下是相同代码的 snake_case 版本,不太可能被误读

if (old_man_shaven) {
  return true;
}

再举一个例子

if (!isNaN(number)) {
  alert('not a number')
}
else if (!number > 50) {
  alert('number too large')
}
else if (!number < 10) {
  alert('number too small')
}
else {
  // code here
}

我查看了我最早的一些代码以获取撰写这篇文章的灵感。我没有看到很多糟糕的名称,因为我编写代码的方式没有使用名称。我使用了很少的函数,很少使用赋值,并且会滥用变量,让它们做十多件事。代码中有很多名称通常是您正确抽象事物的标志。

以下是我代码中的一个示例

function validateNumber(number) {
  var maximumValue = 50;
  var minimumValue = 10;
  if (isNaN(number)) {
    alert('not a number')
    return false;
  }
  if (number > maximumValue) {
    alert('number too large')
    return false;
  }
  if (number < minimumValue) {
    alert('number too small')
    return false;
  }
}

if (validateNumber(number)) {
  // code here
}

注意事项

命名事物是一门艺术,而不是科学。在评估名称是好是坏时,需要考虑名称之外的一些因素。

上下文

上下文可以赋予通用术语更多的含义。例如,“item”是一个模糊的名称,但在 getCustomerSalesOrder() 函数的上下文中,我们可以知道它可能指的是购买的产品。一个简短、集中并考虑上下文的函数可以减少对冗长名称的需求。我仍然更喜欢一个好的名称。随着时间的推移,随着函数变得更长或重构,上下文很容易消失。名称是更永久的含义标记。

注释

代码注释对于可读代码很重要,但它们不能独立完成所有工作。注释应该接续名称未完成的部分,提供无法塞进名称中的细节,说明执行某件事的特定方式的原因,或抱怨某个损坏的库。

/* This refers to a product that was purchased and relates to the customer-sales-order class. */
.product-item {
  display: block;
  text-transform: uppercase;
  width: 100vw;
}

阅读长度

较长的名称会产生更多需要阅读的内容和更宽的行。当深入访问数组时,它尤其成问题,例如

$order_details['customer']['ship_to_address']['default']['street_address']['line_1']

也就是说,即使我刚才给出的那个稻草人示例,虽然冗长,但非常清晰,并且没有让我有任何理由停止阅读以思考或查看函数的其余部分以了解上下文。

代码中的名称随处可见

代码文件中的大部分字符可能不是括号或语法,而是名称。它可能是变量名、函数名、HTML 标签或属性、数据库表或列,或者任何我们在任何给定项目中命名的无数事物。

我仍然在努力命名事物。我经常发现自己坐在文本编辑器前一动不动,试图为某个次要变量命名。我了解到,当发生这种情况时,很可能是因为我正在处理难以概念化的事情,这使得为它找到合适的名称变得更加重要。