Stop Naming Your Variables "Flag": The Art of Boolean Prefixes
Stop Naming Your Variables “Flag”: The Art of Boolean Prefixes
别再把变量命名为 “flag” 了:布尔值命名的艺术
The Problem
问题所在
You inherit a codebase and find this: 你接手了一段代码库,发现了这样的内容:
public class OrderProcessor {
private bool open;
private bool flag;
private bool done;
private bool status;
public void Process(bool check) {
if (open && flag) {
flag = false;
status = true;
}
if (done) {
// what happens here?
}
}
}
Readable? No. What is open? Is the store open? Should the file open? What is flag? That’s a variable screaming “I’ll fix this later.” And is done a question (“is it done?”) or a command (“mark it done”)?
可读吗?完全不可读。open 是什么意思?是商店营业了吗?还是应该打开文件?flag 又是什么?那是一个在尖叫着“我以后再来修”的变量。还有 done,它是一个问题(“完成了吗?”)还是一个命令(“标记为完成”)?
Boolean names are ambiguous land mines. They look innocent until you have to figure out what they mean during a 3 AM outage. Ambiguity breeds bugs. A reviewer looks at if (done) and guesses the intent. A maintainer rewrites flag = false and accidentally inverts logic they didn’t understand. if (!flag)… does that mean turn it on? Turn it off?
布尔值命名就像模糊的地雷。它们看起来人畜无害,直到你在凌晨三点遇到系统故障需要排查时,才会发现它们有多坑人。歧义滋生 Bug。代码审查者看到 if (done) 时只能猜测意图;维护者重写 flag = false 时,可能会不小心反转了他们没理解的逻辑。if (!flag)……这到底是开启还是关闭?
Boolean names matter because they aren’t abstract labels; they are questions. Your code asks the question, and the boolean value answers “yes” or “no.” If the variable name isn’t a clear question, the answer is meaningless. 布尔值的命名至关重要,因为它们不是抽象的标签,而是问题。你的代码在提问,而布尔值负责回答“是”或“否”。如果变量名不是一个清晰的问题,那么答案也就毫无意义。
The Solution: The 4 Prefixes
解决方案:4 个前缀
You can cover 99% of boolean naming scenarios with just four prefixes. If you stick to these, every boolean becomes a clear, grammatical question. 你只需要四个前缀就能覆盖 99% 的布尔值命名场景。只要坚持使用这些前缀,每个布尔值都会变成一个清晰、符合语法的提问。
1. IS: Identity and State
Use is when describing what something is right now. It usually pairs with an adjective.
1. IS:身份与状态
当描述某物当前的状态时使用 is。它通常与形容词搭配。
- Good:
isActive,isDeleted,isEmpty. - Bad:
isAccess(Grammar mismatch. UsehasAccess). - 好:
isActive(是否激活)、isDeleted(是否已删除)、isEmpty(是否为空)。 - 坏:
isAccess(语法不匹配,应使用hasAccess)。
2. HAS: Containment and Features
Use has when describing ownership or inclusion. It pairs with a noun.
2. HAS:包含与特性
当描述所有权或包含关系时使用 has。它与名词搭配。
- Good:
hasAccess,hasChildren,hasValidationErrors. - Bad:
hasActive(Grammar mismatch. UseisActive). - 好:
hasAccess(是否有权限)、hasChildren(是否有子项)、hasValidationErrors(是否有验证错误)。 - 坏:
hasActive(语法不匹配,应使用isActive)。
3. CAN: Capability
Use can to check permissions or potential actions.
3. CAN:能力
使用 can 来检查权限或潜在的操作。
- Good:
canEdit,canDelete,canRetry. - Bad:
canAdmin(Vague. UseisAdminorcanAdminister). - 好:
canEdit(能否编辑)、canDelete(能否删除)、canRetry(能否重试)。 - 坏:
canAdmin(含义模糊,应使用isAdmin或canAdminister)。
4. SHOULD: Intent
Use should for business rules or decisions the system needs to make. This separates “what we can do” from “what we want to do.”
4. SHOULD:意图
使用 should 来表示业务规则或系统需要做出的决策。这能将“我们能做什么”与“我们想做什么”区分开来。
- Good:
shouldRetry,shouldCacheResponse. - Bad:
shouldUser(Incomplete.shouldCreateUser?). - 好:
shouldRetry(是否应该重试)、shouldCacheResponse(是否应该缓存响应)。 - 坏:
shouldUser(不完整,应为shouldCreateUser?)。
Stick to this list. When you mix them up—like writing isAccess or hasActive—you force the reader to stop and re-parse the sentence in their head. That friction is where bugs get introduced.
请坚持使用这个列表。当你混用它们时——比如写成 isAccess 或 hasActive——你会强迫读者停下来,在脑海中重新解析这个句子。这种认知摩擦正是 Bug 产生的地方。
| Prefix | Domain | Function | Example |
|---|---|---|---|
| IS | Identity / State | Describes what an object is currently. | isActive, isEmpty |
| HAS | Containment | Describes ownership or feature presence. | hasChildren, hasAccess |
| CAN | Capability | Describes permission or potential action. | canEdit, canRetry |
| SHOULD | Intent / Logic | Describes a business rule recommendation. | shouldCache, shouldRetry |
| 前缀 | 领域 | 功能 | 示例 |
|---|---|---|---|
| IS | 身份/状态 | 描述对象当前是什么。 | isActive, isEmpty |
| HAS | 包含 | 描述所有权或特性的存在。 | hasChildren, hasAccess |
| CAN | 能力 | 描述权限或潜在操作。 | canEdit, canRetry |
| SHOULD | 意图/逻辑 | 描述业务规则建议。 | shouldCache, shouldRetry |
The “No Negatives” Rule
“禁止否定”规则
Here is the most important rule: Never use negation in the variable name. Avoid names like isNotEnabled, hasNoAccess, or isDisabled.
这是最重要的一条规则:永远不要在变量名中使用否定词。避免使用 isNotEnabled、hasNoAccess 或 isDisabled 这样的名字。
Why? Because eventually, you will need to check for the opposite state. if (!isDisabled) forces your brain to calculate a double negative: “If not is disabled…” Compare that to: if (isEnabled). Immediate clarity.
为什么?因为最终你总会需要检查相反的状态。if (!isDisabled) 会强迫你的大脑去计算双重否定:“如果不是被禁用的……” 相比之下,if (isEnabled) 则一目了然。
Negative names feel natural when you write them (“I need to check if this is disabled”), but they are a tax on every person who reads the code later. Refactoring tools make this worse; if you invert an if statement, your IDE might helpfully rename isDisabled to isNotDisabled. Now you have code that is actively hostile to human readers.
否定式命名在编写时感觉很自然(“我需要检查这是否被禁用了”),但它们对之后阅读代码的每一个人来说都是一种负担。重构工具会让情况变得更糟;如果你反转一个 if 语句,IDE 可能会“好心”地将 isDisabled 重命名为 isNotDisabled。现在,你的代码对人类读者来说简直是充满敌意。
The Exception: The only time a negative name is acceptable is when you are mirroring an external API or HTML attribute that is negative by default, like noValidate. Even then, isolate it at the boundary. In your domain logic, always map it to a positive: bool shouldValidate = !request.noValidate.
例外情况: 只有当你需要映射外部 API 或默认就是否定形式的 HTML 属性(如 noValidate)时,否定式命名才是可以接受的。即便如此,也要将其隔离在边界处。在你的领域逻辑中,始终将其映射为肯定形式:bool shouldValidate = !request.noValidate。
Context Matters (The “Boolean Trap”)
上下文很重要(“布尔陷阱”)
The rules above work perfectly for properties (state). They fall apart for parameters (function arguments). This is the “Boolean Trap”: 上述规则对于属性(状态)非常有效,但对于参数(函数参数)却行不通。这就是所谓的“布尔陷阱”:
// Real code from NHibernate
schemaExport.Execute(false, true, false);
Quick: What does the second true do? You have no idea. You have to open the library source code to find out. If you see a method signature like Execute(bool, bool, bool), you are looking at a design bug.
快问:第二个 true 是做什么的?你根本不知道。你必须打开库的源代码才能查明。如果你看到像 Execute(bool, bool, bool) 这样的方法签名,说明你遇到了设计缺陷。
How to fix it: 如何修复:
-
Split the method: If the boolean changes the method’s behavior entirely, make two methods.
email.SendImmediately(message);/email.SendQueued(message); -
拆分方法: 如果布尔值完全改变了方法的行为,就拆分成两个方法。
email.SendImmediately(message);/email.SendQueued(message); -
Use an Enum: If there are modes, name them.
file.Write(data, WriteMode.Append); -
使用枚举: 如果存在多种模式,请为它们命名。
file.Write(data, WriteMode.Append); -
Use a Config Object: If you have multiple flags, pass an object.
export.Execute(new ExportOptions { Script = false, Export = true, JustDrop = false }); -
使用配置对象: 如果有多个标志,请传递一个对象。
export.Execute(new ExportOptions { Script = false, Export = true, JustDrop = false });
The Antipatterns
反模式
If you want to clean up your code, watch out for these specific smells in code reviews. 如果你想清理代码,请在代码审查中留意这些特定的“坏味道”。
-
The “Shrug” Variable: Names like
flag,done, orchecktell you nothing about what is being tracked.- Bad:
if (check)-> Fix:if (isPaymentVerified)
- Bad:
-
“耸肩”变量: 像
flag、done或check这样的名字,完全无法告诉你正在追踪什么。- 坏:
if (check)-> 好:if (isPaymentVerified)
- 坏:
-
Mismatched Grammar: Breaking the prefix rules confuses the mental model.
- Bad:
if (user.hasActive)-> Fix:if (user.isActive)
- Bad:
-
语法不匹配: 打破前缀规则会扰乱思维模型。
- 坏:
if (user.hasActive)-> 好:if (user.isActive)
- 坏:
-
The Double Negative: Any time you see a
!next to aNotorNo.- Bad:
if (!isNotEnabled)-> Fix:if (isEnabled)
- Bad:
-
双重否定: 任何时候看到
!紧挨着Not或No。- 坏:
if (!isNotEnabled)-> 好:if (isEnabled)
- 坏:
-
The Multi-Purpose Boolean: A single variable that secretly tracks three different things.
- Fix: Be explicit.
bool isReadyForBilling = user.Exists && user.HasEmail && user.IsActive;
- Fix: Be explicit.
-
多用途布尔值: 一个变量偷偷追踪了三件不同的事情。
- 好:明确表达。
bool isReadyForBilling = user.Exists && user.HasEmail && user.IsActive;
- 好:明确表达。
-
The Drifting Flag: A local boolean that gets reused for different logic steps.
- Fix: Use a Result object or return early. Don’t recycle a boolean bucket.
-
漂移标志: 一个在不同逻辑步骤中被重复使用的局部布尔变量。
- 好:使用结果对象或提前返回。不要循环利用一个布尔“桶”。
Conclusion
结论
Naming isn’t about style. It’s about empathy. When you name a boolean flag, you’re writing for yourself in the moment. When you name it isProcessed, you’re writing for the poor soul who has to fix a bug in this file six months from now. That person is probably you. Be kind to your future self.
命名无关风格,而关乎同理心。当你命名一个布尔标志时,你是在为当下的自己编写代码;而当你将其命名为 isProcessed 时,你是在为六个月后不得不修复此文件 Bug 的那个可怜人编写代码。那个人很可能就是你自己。请善待未来的自己。