乐大大
2023/04/27阅读:14主题:重影
命名:每个开发者的噩梦
命名:每个开发者的噩梦
欢迎关注我的公众号“云原生拓展”,原创技术文章第一时间推送。

啊..命名。开发人员最喜欢的脑力运动,介于参加没完没了的会议和重构代码之间。作为开发人员,我们知道命名既是福也是祸。这是我们工作的关键部分,但它可能会导致一些搞笑但很可能令人沮丧的名字。在这篇文章中,我将展示一种我们可以考虑变量的方法以及如何有效地使用它们。即使您对命名变量很有信心,您是否也在充分发挥它们的潜力? 🤔

拯救世界的命名模式
当您发现自己难以为特定变量、函数或类命名时,请考虑使用以下模式并问自己相应的问题。模式是 [scope][typePrefix][baseName][qualifier][typeSuffix]
1. Scope
-
这个变量的可见性或可访问性是什么? (例如,私有、公共、受保护、内部、包私有) (e.g., private, public, protected, internal, package-private) -
此变量是否特定于任何编程语言功能或约定? (例如,Python 中的双下划线前缀、jQuery 中的美元符号、Ruby 中的实例变量“@”) -
这个可变部分是需要特定命名模式的框架或库的一部分吗? (例如,Angular 指令的“ng”前缀,React 钩子的“use”前缀)
2. TypePrefix
-
这个变量是布尔值还是返回布尔值的函数? (例如,是、有、包含、是、整合)(e.g., is, has, contains, are, integrates) -
变量是否表示可以用“是”、“有”、“包含”或“整合”等词描述的状态、条件或动作? (例如,isEnabled、hasAccess、containsItem)(e.g., isEnabled, hasAccess, containsItem) -
这是一个负责获取、设置、获取或更新数据的函数吗? (例如,获取、设置、获取、更新、检索、存储、保存) (e.g., get, set, fetch, update, retrieve, store, save)
3. BaseName
-
这个变量的主要目的是什么? (例如,用户、距离、付款、错误消息)(e.g., user, distance, payment, errorMessage) -
我能否用一个清晰、简洁的词或短语来描述变量在代码中的作用? (例如,registrationStatus、totalPrice、elapsedTime)
4. Qualifier
-
是否有任何其他细节或上下文有助于将此变量与具有类似目的的其他变量区分开来? (例如,名字、姓氏、电话号码)(e.g., firstName, lastName, phoneNumber) -
变量是否具有应包含在名称中的特定单位或属性,例如“InMeters”或“InSeconds”? (例如,distanceInMiles、timeInSeconds) (e.g., distanceInMiles, timeInSeconds) -
这个变量是否代表一个特定的状态或条件,例如“有效”或“可移动”? (例如,isValidEmail、isRemovableItem)
5. TypeSuffix
-
这个变量的基本目的或结构是什么? (例如,Count、Index、Sum、Average、List、Map、Array、Set、Queue) -
是否可以通过为“Count”、“Index”、“Sum”、“Average”、“List”或“Map”等结构添加后缀来明确变量的作用? (例如,itemCount、currentIndex、totalPriceSum、ratingAverage、userList、settingsMap)
这里有6个例子:
1. __isUserLoggedIn:
-
scope: __(Python 私有变量) -
typePrefix: is (boolean) -
baseName: User baseName -
qualifier: LoggedIn 登录
2. fetchProductById:
-
scope: “” (公共方法在大多数语言中没有特定的前缀) -
typePrefix: fetch (function) 获取(函数) -
baseName: Product 产品 -
qualifier: ById
3. updateEmailPreference: updateEmailPreference :
-
scope: “” (public) -
typePrefix: update (function) 更新(函数) -
baseName: Email -
qualifier: Preference
4. $distanceInMetersInput:
-
scope: $ (jQuery variable) -
typePrefix: “” -
baseName: distance 距离 -
qualifier: InMeters -
typeSuffix: Input 输入
5. getUserByEmail: getUserByEmail :
-
typePrefix: get (function) 获取(函数) -
baseName: User -
qualifier: ByEmail 通过电子邮件
有时您可能需要交换 baseName 和 qualifier,以便为变量提供更合适和更有意义的名称。示例:getLoggedInUser 而不是 getUserLoggedIn。
有效命名变量
好吧,你已经有了一个可靠的变量命名计划😎。但这只是成功的一半。另一半?注释。但不是你想的那样。
您可能听说过在整个代码中散布注释以使其更易于理解的建议,尤其是对于您未来的自己。但是,实际上,这并不总是最好的方法。
考虑一下:如果您的代码在没有注释的情况下难以理解,那么问题不在于缺少注释。如果您的代码已经很清楚,那么您一开始就不需要注释。
那么,我要表达的意思是什么?使用变量作为注释。
当您将所有内容存储在具有有意义名称的变量中时,通常只需阅读这些名称和关键控制结构(如 if 语句)即可理解整个代码。让我给你看一个例子:
前几天,我遇到一段代码,我花了大约 10 分钟才完全掌握 🥴。
if (!row.doc[otherField]) {
let val;
if(currentField == "left") {
val = row.doc[currentField].charAt(0) === "-" ? row.doc[currentField].slice(1) : row.doc[currentField];
}
if(currentField == "right") {
val = row.doc[currentField].charAt(0) === "-" ? row.doc[currentField] : `-${row.doc[currentField]}`;
}
row.doc[otherField] = val === "-0" ? "0" : val;
row.refreshField(otherField);
}
if (currentField === "left" && row.doc["left"] && row.doc["left"].charAt(0) !== "-") {
row.doc["left"] = row.doc["left"] === "0" ? row.doc["left"] : `-${row.doc["left"]}`;
row.refreshField("left");
}
if (currentField === "right" && row.doc["right"] && row.doc["right"].charAt(0) === "-") {
row.doc["right"] = row.doc["right"].slice(1);
row.refreshField("right");
}
因此,让我们通过将内容放入变量并为它们指定有意义的名称来清理它:
const valueOfOtherField = row.doc[otherField];
const valueOfCurrentField = row.doc[currentField];
const valueOfLeftField = row.doc["left"];
const valueOfRightField = row.doc["right"];
const isCurrentFieldOnLeft = currentField === "left";
const isCurrentFieldOnRight = currentField === "right";
const startsWithMinusSign = (str) => str.charAt(0) === "-";
const removeMinusFromZero = (str) => str === "-0" ? "0" : str;
const ensureMinusAtStart = (str) => startsWithMinusSign(str) ? str : `-${str}`;
const removeMinusFromStart = (str) => str.replace(/^-/, "");
if (!valueOfOtherField) {
let val;
if (isCurrentFieldOnLeft) {
val = startsWithMinusSign(valueOfCurrentField) ?
removeMinusFromStart(valueOfCurrentField) :
valueOfCurrentField;
} else if (isCurrentFieldOnRight) {
val = startsWithMinusSign(valueOfCurrentField) ?
valueOfCurrentField :
ensureMinusAtStart(valueOfCurrentField);
}
row.doc[otherField] = removeMinusFromZero(val);
row.refreshField(otherField);
}
if (isCurrentFieldOnLeft && valueOfLeftField && !startsWithMinusSign(valueOfLeftField)) {
row.doc["left"] = removeMinusFromZero(ensureMinusAtStart(valueOfLeftField));
row.refreshField("left");
}
if (isCurrentFieldOnRight && valueOfRightField && startsWithMinusSign(valueOfRightField)) {
row.doc["right"] = removeMinusFromStart(valueOfRightField);
row.refreshField("right");
}
通过此更改,代码读起来几乎像普通英语一样。然而,经过仔细检查,我们可以识别出一些可以删除和简化的不必要的逻辑步骤。以这一行为例:
val = startsWithMinusSign(valueOfCurrentField) ?
valueOfCurrentField :
ensureMinusAtStart(valueOfCurrentField);
从逻辑上讲,还可以进一步简化。如果值以负号开头,我们保留它;否则,我们添加一个新的减号。本质上,这意味着我们可以简单地在开头添加一个减号。因此,该行可以简化为:
val = ensureMinusAtStart(valueOfCurrentField);
假设 row.refreshField
的性能可以忽略不计,可以通过删除 && startsWithMinusSign(valueOfRightField)
和 && !startsWithMinusSign(valueOfLeftField)
条件将相同的逻辑应用于 if 语句。整个代码现在应该是这样的:
const valueOfOtherField = row.doc[otherField];
const valueOfCurrentField = row.doc[currentField];
const valueOfLeftField = row.doc["left"];
const valueOfRightField = row.doc["right"];
const isCurrentFieldOnLeft = currentField === "left";
const isCurrentFieldOnRight = currentField === "right";
const startsWithMinusSign = (str) => str.charAt(0) === "-";
const removeMinusFromZero = (str) => str === "-0" ? "0" : str;
const ensureMinusAtStart = (str) => removeMinusFromZero(
startsWithMinusSign(str) ? str : `-${str}`
);
const removeMinusFromStart = (str) => str.replace(/^-/, "");
if (!valueOfOtherField) {
// If the current input field is on the left, let's remove the minus from the start
// and if its on the right, let's add the minus at the start. And put it into the
// other field. (NOTE: In the original code there were exactly two fields, left and right.)
const newValue = isCurrentFieldOnLeft ?
removeMinusFromStart(valueOfCurrentField) :
ensureMinusAtStart(valueOfCurrentField);
row.doc[otherField] = newValue;
row.refreshField(otherField);
}
// If the current field is the left one and there is an inputted value then
// make sure to add the minus at the start
if (isCurrentFieldOnLeft && valueOfLeftField) {
row.doc["left"] = ensureMinusAtStart(valueOfLeftField);
row.refreshField("left");
}
// If the current field is the right one and there is an inputted value then
// make sure to remove the minus from the start
if (isCurrentFieldOnRight && valueOfRightField) {
row.doc["right"] = removeMinusFromStart(valueOfRightField);
row.refreshField("right");
}
在这一点上,我们可以轻松地阅读代码而不用撕毁我们的头发。该代码完成了两个主要任务:
-
它确保当用户在左侧和右侧字段中输入值时,右侧始终为正,左侧始终为负。
-
如果其中一个字段尚未填写,代码会将当前输入字段的值复制到另一个字段。
用原始代码理解这种行为会非常具有挑战性。然而,通过更有效地使用变量,我们可以抽象出复杂的语法混乱,让代码读起来更自然 💪。
在评论中让我知道你的想法。也许你有更多的建议。如果您觉得这篇文章有帮助,请考虑喜欢它并在以后关注更多文章。您的支持意义重大,并鼓励我继续分享更多❤。编码愉快!

作者介绍