10代码整洁之道

类

类设计

类组织封装内聚

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

第10章 类

一、导读

1.1 本章学习目标

第10章讲解了类设计的艺术。Uncle Bob 介绍了类的组织原则:类应该短小、单一职责、高内聚。本章的核心学习目标包括:

  • 掌握类应该短小:比函数更短。
  • 理解单一职责原则(SRP):一个类只有一个变更理由。
  • 学会内聚性:类的字段和方法应该紧密相关。
  • 掌握封装:隐藏实现细节。
  • 理解依赖关系:最小化类的依赖。
  • 学会类的组织:字段、公共方法、私有方法。

1.2 为什么本章如此重要

类是面向对象编程的基本构建块。Uncle Bob 指出:类的设计质量直接决定了系统的可维护性。一个好的类应该职责清晰、接口简洁、实现封装。

本章的重要性在于它提供了类设计的系统化方法。你会理解如何设计高质量的类,如何避免"上帝类",如何实现良好的封装。

1.3 核心问题引导

你的类有多"大"?有没有超过500行的?

你的类是否遵循单一职责原则?

类的内聚性如何?字段和方法是否紧密相关?

封装做得如何?内部细节是否暴露了?

类的依赖关系是否合理?


二、核心概念详解

2.1 类应该短小

2.1.1 短小的标准

Uncle Bob 建议:类比函数更应该短小。函数的理想长度是20行,类的理想长度是100-200行。

java
// 差:过大的类
public class UserService {
  // 500行代码
  // 用户验证
  // 用户创建
  // 用户修改
  // 用户删除
  // 用户查询
  // 用户通知
  // 用户日志
  // 用户统计
  // ...
}

// 好:短小的类
public class UserService {
  private final UserValidator validator;
  private final UserRepository repository;
  private final UserNotifier notifier;
  
  public UserResult createUser(CreateUserRequest request) {
    validator.validate(request);
    User user = User.create(request);
    repository.save(user);
    notifier.sendWelcome(user);
    return UserResult.success(user.getId());
  }
}

public class UserValidator {
  public void validate(CreateUserRequest request) {
    // 只负责验证
  }
}

public class UserRepository {
  public void save(User user) {
    // 只负责持久化
  }
}

public class UserNotifier {
  public void sendWelcome(User user) {
    // 只负责通知
  }
}

2.2 单一职责原则

2.2.1 一个类只有一个变更理由

单一职责原则(SRP):一个类应该只有一个变更的理由。

java
// 差:多个变更理由
public class User {
  // 变更理由1:用户数据模型变化
  private String name;
  private String email;
  
  // 变更理由2:验证规则变化
  public boolean isValid() {
    return name != null && email != null && email.contains("@");
  }
  
  // 变更理由3:持久化方式变化
  public void save() {
    // SQL 语句
    // 直接操作数据库
  }
  
  // 变更理由4:通知方式变化
  public void sendNotification(String message) {
    // 发送邮件
  }
}

// 好:每个类只有一个变更理由
public class User {
  // 只有数据模型变化时才修改
  private final String name;
  private final String email;
  
  public String getName() { return name; }
  public String getEmail() { return email; }
}

public class UserValidator {
  // 只有验证规则变化时才修改
  public boolean isValid(User user) {
    return user.getName() != null 
        && user.getEmail() != null 
        && user.getEmail().contains("@");
  }
}

public class UserRepository {
  // 只有持久化方式变化时才修改
  public void save(User user) {
    // SQL 语句
  }
}

public class UserNotifier {
  // 只有通知方式变化时才修改
  public void sendNotification(User user, String message) {
    // 发送邮件
  }
}

2.3 内聚性

2.3.1 高内聚的类

高内聚意味着类的字段和方法紧密相关,共同服务于一个目的。

java
// 差:低内聚
public class Employee {
  // 员工数据
  private String name;
  private double salary;
  
  // 税务计算(不紧密相关)
  public double calculateTax() {
    return salary * 0.2;
  }
  
  // 报告生成(不紧密相关)
  public String generateReport() {
    return "Employee: " + name + ", Salary: " + salary;
  }
  
  // 数据库操作(不紧密相关)
  public void saveToDatabase() {
    // SQL
  }
}

// 好:高内聚
public class Employee {
  private String name;
  private Money salary;
  
  public String getName() { return name; }
  public Money getSalary() { return salary; }
  
  public void giveRaise(Money amount) {
    salary = salary.add(amount);
  }
  
  // 所有方法都围绕"员工数据"这个核心
}

public class TaxCalculator {
  public Money calculateTax(Employee employee) {
    return employee.getSalary().multiply(0.2);
  }
}

public class EmployeeReportGenerator {
  public String generate(Employee employee) {
    return "Employee: " + employee.getName() + 
           ", Salary: " + employee.getSalary();
  }
}

2.4 封装

2.4.1 隐藏实现细节

java
// 差:暴露内部细节
public class Stack {
  public List<Object> elements; // 暴露内部数据结构
  
  public void push(Object element) {
    elements.add(element);
  }
}

// 外部可以直接修改内部数据
Stack stack = new Stack();
stack.elements.clear(); // 绕过 push 方法

// 好:良好封装
public class Stack {
  private final List<Object> elements = new ArrayList<>();
  
  public void push(Object element) {
    elements.add(element);
  }
  
  public Object pop() {
    if (elements.isEmpty()) {
      throw new EmptyStackException();
    }
    return elements.remove(elements.size() - 1);
  }
  
  public boolean isEmpty() {
    return elements.isEmpty();
  }
  
  // 内部实现可以随意修改
  // 比如改为数组,不影响外部代码
}

2.5 依赖关系

2.5.1 最小化依赖

java
// 差:过多依赖
public class OrderProcessor {
  private final DatabaseConnection dbConn;
  private final SmtpClient smtpClient;
  private final FileSystem fileSystem;
  private final Logger logger;
  private final ConfigManager config;
  private final CacheManager cache;
  // ... 20个依赖
  
  public OrderProcessor() {
    // 直接创建依赖
    this.dbConn = new DatabaseConnection("jdbc:mysql://...");
    this.smtpClient = new SmtpClient("smtp.example.com");
    // ...
  }
}

// 好:最小化依赖
public class OrderProcessor {
  private final OrderRepository repository;
  private final NotificationService notifier;
  
  public OrderProcessor(OrderRepository repository,
                        NotificationService notifier) {
    this.repository = repository;
    this.notifier = notifier;
  }
  
  public void process(Order order) {
    repository.save(order);
    notifier.sendConfirmation(order);
  }
}

2.6 类的组织

2.6.1 标准的类结构

java
public class OrderService {
  
  // 1. 常量
  private static final int MAX_RETRY_ATTEMPTS = 3;
  private static final Money MINIMUM_ORDER_AMOUNT = Money.of(10);
  
  // 2. 静态字段
  private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
  
  // 3. 实例字段
  private final OrderRepository orderRepository;
  private final PaymentGateway paymentGateway;
  private final NotificationService notificationService;
  
  // 4. 构造函数
  public OrderService(OrderRepository orderRepository,
                      PaymentGateway paymentGateway,
                      NotificationService notificationService) {
    this.orderRepository = orderRepository;
    this.paymentGateway = paymentGateway;
    this.notificationService = notificationService;
  }
  
  // 5. 公共方法
  public OrderResult placeOrder(CreateOrderRequest request) {
    validateRequest(request);
    Order order = createOrder(request);
    orderRepository.save(order);
    
    PaymentResult payment = paymentGateway.charge(
        order.getCustomer(), order.getTotalAmount());
    
    if (payment.isSuccess()) {
      order.confirm();
      orderRepository.save(order);
      notificationService.sendConfirmation(order);
      return OrderResult.success(order.getId());
    } else {
      order.cancel();
      orderRepository.save(order);
      return OrderResult.failure(payment.getErrorMessage());
    }
  }
  
  // 6. 私有方法
  private void validateRequest(CreateOrderRequest request) {
    if (request == null) {
      throw new IllegalArgumentException("Request must not be null");
    }
    if (request.getItems().isEmpty()) {
      throw new IllegalArgumentException("Order must have items");
    }
  }
  
  private Order createOrder(CreateOrderRequest request) {
    return new Order(
        OrderId.generate(),
        request.getCustomerId(),
        request.getItems()
    );
  }
}

三、实战案例

3.1 案例一:拆分上帝类

java
// 改善前:上帝类
public class UserManager {
  // 用户数据
  private String name;
  private String email;
  private String password;
  
  // 用户验证
  public boolean validate() { ... }
  public boolean isEmailValid() { ... }
  public boolean isPasswordStrong() { ... }
  
  // 用户持久化
  public void save() { ... }
  public void delete() { ... }
  public User findById(int id) { ... }
  
  // 用户通知
  public void sendWelcomeEmail() { ... }
  public void sendPasswordReset() { ... }
  
  // 用户统计
  public int countActiveUsers() { ... }
  public List<User> findRecentUsers() { ... }
}

// 改善后:拆分
public class User {
  private final String name;
  private final String email;
  private final EncryptedPassword password;
  
  public String getName() { return name; }
  public String getEmail() { return email; }
}

public class UserValidator {
  public ValidationResult validate(User user) { ... }
  public boolean isEmailValid(String email) { ... }
  public boolean isPasswordStrong(String password) { ... }
}

public class UserRepository {
  public void save(User user) { ... }
  public void delete(UserId id) { ... }
  public Optional<User> findById(UserId id) { ... }
}

public class UserNotifier {
  public void sendWelcomeEmail(User user) { ... }
  public void sendPasswordReset(User user) { ... }
}

public class UserStatistics {
  public int countActiveUsers() { ... }
  public List<User> findRecentUsers() { ... }
}

四、本章小结

第10章讲解了类设计的艺术,核心要点包括:

类应该短小:ideally 100-200行。

单一职责原则:一个类只有一个变更理由。

高内聚:字段和方法紧密相关。

封装:隐藏实现细节,暴露稳定接口。

最小化依赖:减少类的依赖数量。

类的组织:常量→静态字段→实例字段→构造函数→公共方法→私有方法。

Uncle Bob 的核心观点是:好的类设计是系统可维护性的基础。


五、思考与练习

思考题

你的类有多"大"?有没有超过500行的?

你的类是否遵循单一职责原则?

类的内聚性如何?

编程练习

找到一个"上帝类",将其拆分为多个单一职责的类。

为一个"图书馆管理系统"设计类结构。

改善一个类的封装,隐藏内部细节。

10/10类
0%