2代码整洁之道

有意义的命名

命名艺术

命名规则命名约定避免误导

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章 有意义的命名

一、导读

1.1 本章学习目标

第2章深入讲解了命名的艺术。Uncle Bob 强调,好的命名是代码可读性的基础。本章的核心学习目标包括:

  • 掌握命名要名副其实:名称应该揭示意图。
  • 学会避免误导:不使用含义模糊的名称。
  • 理解有意义的区分:避免使用数字序列。
  • 掌握可搜索的名称:避免单字母变量。
  • 学会类名和方法名的约定:类名用名词,方法名用动词。
  • 理解避免编码信息:让名称自然表达含义。

1.2 为什么本章如此重要

命名是编程中最基本也最重要的技能。Uncle Bob 指出:好的命名让代码自解释,减少注释的需要。一个精心选择的名称可以替代一段注释,让代码更简洁、更清晰。

命名之所以困难,是因为它要求我们深入理解代码的本质。一个好的名称必须准确地反映变量、函数或类的用途。这种准确性来自于对问题的深入理解。

1.3 核心问题引导

你的变量名是否能让人一看就明白它的用途?

你见过哪些误导性的命名?它们造成了什么问题?

如何做到"名副其实"?命名的标准是什么?

单字母变量在什么情况下可以使用?

命名约定(如驼峰、下划线)重要吗?如何选择?


二、核心概念详解

2.1 名副其实

2.1.1 名称应该揭示意图

好的名称应该回答:它是什么?它做什么?它为什么存在?

java
// 差:不揭示意图
int d; // 表示什么?经过的时间?距离?日期?
List<int[]> getThem(); // get what them?

// 好:揭示意图
int elapsedTimeInDays;
List<Cell> getFlaggedCells();

// 差的代码
public class Game {
  private int[][] board; // 4x4 的棋盘
  
  public List<int[]> getThem() {
    List<int[]> list = new ArrayList<>();
    for (int[] row : board) {
      if (row[0] == 4) { // 4 是什么?
        list.add(row);
      }
    }
    return list;
  }
}

// 好的代码
public class MineSweeperBoard {
  private Cell[][] cells;
  
  public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : cells) {
      if (cell.isFlagged()) {
        flaggedCells.add(cell);
      }
    }
    return flaggedCells;
  }
}

2.1.2 用领域语言命名

使用问题领域的术语来命名,让代码与业务紧密关联。

java
// 差:技术术语
public class DataProcessor {
  public List<Map<String, Object>> fetchAndTransform(String query) {
    // ...
  }
}

// 好:领域术语
public class OrderRepository {
  public List<OrderSummary> findRecentOrdersForCustomer(CustomerId customerId) {
    // ...
  }
}

2.2 避免误导

2.2.1 不要使用含义模糊的词

java
// 差:模糊的词
public class AccountManager {
  // manage 做什么?创建?删除?查询?修改?
  public void manage(Account account) { ... }
  
  // handle 做什么?
  public void handle(Event event) { ... }
  
  // process 做什么?
  public void process(Data data) { ... }
}

// 好:具体的词
public class AccountService {
  public void createAccount(Account account) { ... }
  public void deleteAccount(AccountId id) { ... }
  public Account findAccount(AccountId id) { ... }
  public void updateAccount(Account account) { ... }
}

2.2.2 不要使用容易混淆的名称

java
// 差:容易混淆
// XYZZangle 和 XYZangle 有什么区别?
// 在代码中搜索 "a" 会找到大量无关结果
int XYZZangle;

// 好:清晰可区分
int activeAngle;
int expectedAngle;

2.2.3 类型名称不要误导

java
// 差:类型名误导
// accountList 暗示是 List,但实际是 Account 的集合
Account accountList;

// 好:准确反映类型
List<Account> accounts;
AccountCollection accountCollection;

2.3 做有意义的区分

2.3.1 避免数字序列

java
// 差:数字序列
String a1; // 表示什么?
String a2; // 和 a1 有什么区别?

// 好:有意义的区分
String sourceName;
String destinationName;

// 差:无意义的区分
public static void copyChars(char[] a1, char[] a2) {
  // a1 和 a2 有什么区别?
}

// 好:有意义的名称
public static void copyChars(char[] source, char[] destination) {
  // 一目了然
}

2.3.2 避免冗余

java
// 差:冗余
// "Name" 已经暗示了是字符串,不需要 "NameString"
String nameString;

// "Customer" 已经暗示了是对象,不需要 "CustomerObject"
Customer customerObject;

// 好:简洁
String name;
Customer customer;

2.4 使用可搜索的名称

2.4.1 避免单字母变量

java
// 差:单字母变量(循环变量除外)
String e; // 在代码中搜索 "e" 几乎不可能
int n;    // 搜索 "n" 会找到大量无关结果

// 好:可搜索的名称
String errorMessage;
int numberOfItems;

// 例外:短循环中的循环变量
for (int i = 0; i < items.size(); i++) {
  // i 的作用域很小,可以接受
}

2.4.2 常量的命名

java
// 差:魔术数字
if (status == 4) { ... } // 4 是什么?

// 好:命名常量
public static final int STATUS_ACTIVE = 4;
if (status == STATUS_ACTIVE) { ... }

// 更好:使用枚举
public enum OrderStatus {
  CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}
if (status == OrderStatus.CONFIRMED) { ... }

2.5 类名和方法名

2.5.1 类名应该是名词

java
// 差:类名用动词或形容词
public class ProcessOrder { ... }
public class Fast { ... }

// 好:类名用名词或名词短语
public class OrderProcessor { ... }
public class SpeedOptimizer { ... }
public class User { ... }
public class BankAccount { ... }

2.5.2 方法名应该是动词

java
// 差:方法名用名词
public class User {
  public String name() { ... } // 是获取名字还是设置名字?
}

// 好:方法名用动词
public class User {
  public String getName() { ... }      // 获取
  public void setName(String name) { ... } // 设置
  public boolean isActive() { ... }     // 判断
  public void activate() { ... }        // 动作
}

2.5.3 访问器、修改器和断言

java
// 访问器:get
public String getName() { return name; }

// 修改器:set
public void setName(String name) { this.name = name; }

// 断言:is/has/can/should
public boolean isValid() { ... }
public boolean hasPermission() { ... }
public boolean canEdit() { ... }
public boolean shouldRetry() { ... }

2.6 避免编码信息

2.6.1 不需要匈牙利命名法

java
// 差:匈牙利命名法
String sName;    // s 表示 string
int iCount;      // i 表示 int
float fPrice;    // f 表示 float

// 好:让类型系统工作
String name;
int count;
double price;
// 现代 IDE 会显示类型,不需要在名称中编码

2.6.2 不需要成员前缀

java
// 差:成员前缀
public class User {
  private String m_name;
  private int m_age;
  
  public String getName() { return m_name; }
}

// 好:不需要前缀
public class User {
  private String name;
  private int age;
  
  public String getName() { return name; }
}

2.6.3 接口和实现

java
// 差:接口加 I 前缀
public interface IUserService { ... }
public class UserService implements IUserService { ... }

// 好:接口不加前缀,实现加 Impl
public interface UserService { ... }
public class UserServiceImpl implements UserService { ... }

// 或者:接口用描述性名称
public interface OrderRepository { ... }
public class JpaOrderRepository implements OrderRepository { ... }
public class MongoOrderRepository implements OrderRepository { ... }

三、实战案例

3.1 案例一:改善命名

java
// 改善前
public class Calc {
  public double c(double p, double r) {
    return p * r / 100;
  }
  
  public List<Map<String, Object>> gD() {
    List<Map<String, Object>> l = new ArrayList<>();
    // ...
    return l;
  }
}

// 改善后
public class InterestCalculator {
  
  public Money calculateSimpleInterest(Money principal, Rate annualRate) {
    return principal.multiply(annualRate.getValue());
  }
  
  public List<TransactionDetail> getTransactionDetails() {
    List<TransactionDetail> details = new ArrayList<>();
    // ...
    return details;
  }
}

四、本章小结

第2章讲解了命名的艺术,核心要点包括:

名副其实:名称应该揭示意图,让人一看就明白。

避免误导:不使用模糊、混淆或冗余的名称。

有意义的区分:避免数字序列,使用有意义的名称。

可搜索的名称:避免单字母变量,使用完整的名称。

类名用名词,方法名用动词:遵循语言约定。

避免编码信息:让类型系统工作,不需要在名称中编码类型。

Uncle Bob 的核心观点是:命名是编程中最重要的技能之一。好的命名让代码自解释,减少注释的需要。


五、思考与练习

思考题

你的代码中有哪些命名不好的变量/函数/类?如何改善?

你见过哪些误导性的命名?它们造成了什么问题?

命名约定在你的团队中重要吗?如何统一?

编程练习

找到一段代码,改善其中所有的命名。

为一个"图书馆管理系统"设计类名和方法名。

找到代码中所有使用数字序列命名的变量,替换为有意义的名称。

2/10有意义的
0%