30、类与系统设计:类的组织、类应该短小、内聚性原则、将系统构造与使用分离
说实话,我见过太多人把类写得跟"万能工具箱"似的。一个类里塞了十几个方法,什么都能干,但什么都干不干净。今天咱们就聊聊,怎么把类写得短小、内聚,以及如何把系统的"构造"和"使用"拆开。
类的组织:先读起来舒服
我个人习惯,一个类的代码顺序是这样的:
- 公共静态常量(public static final)
- 私有静态变量(private static)
- 私有实例变量(private)
- 公共方法(public)
- 私有工具方法(private)
为什么这么排?因为读代码的人最关心的是"这个类能干什么",而不是"它内部怎么实现的"。公共方法放在前面,就像一本书的目录,先让人看到能力,再看细节。
类应该短小:不是代码行数少,而是职责少
很多人误解了"短小"。不是说一个类只能写50行,而是说它只做一件事。
我在项目中遇到过这样一个例子:一个叫 OrderProcessor 的类,里面既有订单校验、又有库存扣减、还有发送邮件通知。看起来每个方法都不长,但整个类做了三件完全不同的事。后来我把它拆成了三个类:OrderValidator、InventoryManager、NotificationService。每个类都短了,但更重要的是,每个类的职责清晰了。
内聚性原则:方法之间要"抱团"
内聚性,说白了就是:一个类里的方法和变量,应该紧密相关。它们应该为了同一个目标而工作。
举个例子:
// 低内聚:这个类既管用户信息,又管文件上传
public class UserService {
private String userName;
private String email;
public void updateUserInfo() { ... }
public void uploadAvatar(File file) { ... }
public void resizeImage(File file) { ... }
}
// 高内聚:每个类只做一件事
public class UserProfile {
private String userName;
private String email;
public void updateInfo() { ... }
}
public class AvatarService {
public void upload(File file) { ... }
public void resize(File file) { ... }
}
你想想看,如果 UserService 里突然冒出个 resizeImage,新同事看到会不会一脸懵?这就是内聚性被破坏了。
将系统构造与使用分离
这是很多架构问题的根源。什么叫"构造"?就是对象的创建、依赖的组装。什么叫"使用"?就是业务逻辑的执行。
很多人喜欢在业务代码里直接 new 对象:
public class OrderService {
public void createOrder(OrderData data) {
// 构造与使用混在一起
EmailSender sender = new EmailSender("smtp.xxx.com", 587);
sender.send(data.getEmail(), "订单创建成功");
}
}
这样做有什么问题?
- 测试困难:你没法 mock 掉 EmailSender
- 配置硬编码:smtp地址、端口都写死在代码里
- 扩展性差:想换一个邮件服务商,得改业务代码
正确的做法是:把构造交给"工厂"或"依赖注入容器",业务代码只负责使用。
public class OrderService {
private final EmailSender emailSender;
// 构造由外部注入,业务代码只负责使用
public OrderService(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void createOrder(OrderData data) {
emailSender.send(data.getEmail(), "订单创建成功");
}
}
说白了,就是"别在业务代码里 new 东西"。把对象的创建权交给框架或专门的配置类,你的业务逻辑会干净很多。
config 包,里面放所有对象的创建逻辑。业务代码只依赖接口,不依赖具体实现。这样换实现的时候,改一行配置就够了。
知识体系总览
下面这张图,把本章的核心逻辑串起来了:
嗯,这四个点其实是一体的。类的组织是"形式",短小是"尺度",内聚是"原则",构造与使用分离是"架构手段"。把它们结合起来,你的代码会变得容易读、容易改、容易测。