您是否曾经花了几秒钟以上盯着同一条代码?您多久发现有条件棘手的解析?在本文中,我们讨论了混乱的条件在我们的代码中如何表现出来。一路上,我们还探索了不同的重构技术,以提高条件控制流的可读性。
双重负面
当我们否定名称已经以非肯定风格的变量时,就会出现双重负面。考虑以下示例。
// Non-affirmative Naming Convention
const isNotReady = doSomething();
const isForbidden = doSomething();
const cannotJoinRoom = doSomething();
const hasNoPermission = doSomething();
// Negating once results in an awkward double negative situation.
console.log(!isNotReady);
console.log(!isForbidden);
console.log(!cannotJoinRoom);
console.log(!hasNoPermission);
这与
// Affirmative Naming Convention
const isReady = doSomething();
const isAllowed = doSomething();
const canJoinRoom = doSomething();
const hasPermission = doSomething();
// Negation actually makes sense here!
console.log(!isReady);
console.log(!isAllowed);
console.log(!canJoinRoom);
console.log(!hasPermission);
不幸的是,由于标准和向后兼容,有时不可避免的命名约定是不可避免的。以HTML DOM APIs的koude0属性为例。 <input>
标签中disabled
属性的存在告诉浏览器(在视觉和字面上)禁用元素的形式控制。否则,它的缺席会导致<input>
表现出其默认行为,即接受用户输入。这是html的人体工程学的不幸副作用。
<!-- Normal Checkbox (Default) -->
<input type="checkbox" />
<!-- Disabled Checkbox -->
<input type="checkbox" disabled />
尽管如此,我们仍然应该尽可能地争取肯定的命名惯例。它们更容易阅读和解析,仅仅是因为无需始终在精神上否定变量名称。此规则适用于两个变量名称和函数名称。
非肯定控制流
下一种双重负面形式更加微妙。
// Suppose that we _do_ follow affirmative naming conventions.
const isAllowed = checkSomething();
if (!isAllowed) {
// There is something funny about this...
doError();
} else {
// Notice how the `else` block is practically a double negation?
doSuccess();
}
如上所述,非肯定的样式也可以弥补条件控制流。回想一下,else
块实际上是对相应的if
条件的否定。因此,我们必须在这里扩展肯定风格。修复实际上很简单。
// Just invert the logic!
const isAllowed = checkSomething();
if (isAllowed) {
doSuccess();
} else {
doError();
}
同一规则适用于平等和不平等检查。
// ❌ Don't do this!
if (value !== 0) {
doError();
} else {
doSuccess();
}
// ✅ Prefer this instead.
if (value === 0) {
doSuccess();
} else {
doError();
}
有些人甚至可能会让有条件的块是空白的,只是为了否定肯定风格的条件。尽管我并不是在提倡每个人都采取这么多的态度,但我可以理解为什么对于某些人来说这可能更可读。以koude6操作员为例,例如,如果没有括号,它就无法轻易否定。
if (obj instanceof Animal) {
// Intentionally left blank.
} else {
// Do actual work here (in the negation).
doSomething();
}
早期回报的例外
快速旁边,有条件控制流的特殊例外。在这种情况下,否定是必要的。
if (!isAllowed) {
// Return early here.
doError();
return;
}
// Otherwise, proceed with the success branch.
doSuccess();
,如果可能的情况下,我们仍然应该尝试将逻辑产生较小的嵌套,缩小水平和更可读的肯定风格。
// Prefer affirmative early returns.
if (isAllowed) {
doSuccess();
return;
}
// If we did not invert the logic, this would have been
// nested inside the `!isAllowed` conditional block.
if (!hasPermission) {
doPermissionError();
return;
}
// When all else fails, do something else.
doSomethingElse();
return;
以肯定样式表达相同控制流的另一种方法(无早期返回)如下。
// Hooray for the affirmative style!
if (isAllowed) {
doSuccess();
} else if (hasPermission) {
doSomethingElse();
} else {
doPermissionError();
}
return;
当然,还有许多其他方法可以交换,倒转和重构代码,每个代码都是完全主观的。因此,保留肯定的惯例将成为某种艺术形式。无论如何,只要我们坚持肯定风格的一般准则,代码的可读性将始终提高。
复合条件
逻辑运营商(例如koude7和koude8),这个故事变得更加复杂。例如,我们如何以更肯定的风格重构下面的代码?
// This is fine... but there has to be a better way,
// right? There are just too many negations here!
if (!isUser || !isGuest) {
doSomething();
} else {
doAnotherThing();
}
对于复合条件,我们介绍了布尔代数最被低估的法律:De Morgan's Laws!
// Suppose that these are **any** two Boolean variables.
let a: boolean;
let b: boolean;
// The following assertions will **always** hold for any
// possible pairing of values for `a` and `b`.
!(a && b) === !a || !b;
!(a || b) === !a && !b;
感谢De Morgan的法律,我们现在有一种方法可以“分发”条件中的否定,然后“翻转”其操作员(从&&
到&&
到||
,反之亦然)。
尽管以下示例仅具有二进制比较(即两个要素),但只要我们尊重operator precedence,de Morgan的定律就可以在任何数量的条件变量上推广。即,
&&
运算符始终在||
操作员之前进行评估。
// By De Morgan's Laws, we can "factor out" the negation as follows.
if (!(isUser && isGuest)) {
doSomething();
} else {
doAnotherThing();
}
// Then we simply invert the logic as we did in the previous section.
if (isUser && isGuest) {
doAnotherThing();
} else {
doSomething();
}
现在,这不是更可读性吗?使用德·摩根(De Morgan)的法律,我们可以清理“否定太多”的条件。
结论
此时总体主题应该显而易见。在可能的情况下,我们应该避免编写代码,以迫使读者跳过(不必要的)需要额外的认知开销的篮球。在本文中,我们讨论了以下技术:
- 鼓励肯定的命名约定。
- 避免使用否定术语/诸如
no
,not
,dis-
,mal-
等的前缀
- 避免使用否定术语/诸如
- 更喜欢正等价。
- 将条件控制流(如有可能)倒转,以适应肯定风格。
- 在交换,倒转和重构分支时随意玩。
- 早期返回 May 需要否定。
- 使用布尔代数中的一些技巧来倒置通风管。
- de Morgan的定律是重构的特别有力工具!
现在要出发,以更清洁的条件祝福世界!