30、类与系统设计:类的组织、类应该短小、内聚性原则、将系统构造与使用分离

说实话,我见过太多人把类写得跟"万能工具箱"似的。一个类里塞了十几个方法,什么都能干,但什么都干不干净。今天咱们就聊聊,怎么把类写得短小、内聚,以及如何把系统的"构造"和"使用"拆开。

类的组织:先读起来舒服

我个人习惯,一个类的代码顺序是这样的:

  • 公共静态常量(public static final)
  • 私有静态变量(private static)
  • 私有实例变量(private)
  • 公共方法(public)
  • 私有工具方法(private)

为什么这么排?因为读代码的人最关心的是"这个类能干什么",而不是"它内部怎么实现的"。公共方法放在前面,就像一本书的目录,先让人看到能力,再看细节。

小技巧: 如果你发现一个类的私有方法比公共方法还多,而且调用关系复杂,那这个类很可能"太胖了",该拆了。

类应该短小:不是代码行数少,而是职责少

很多人误解了"短小"。不是说一个类只能写50行,而是说它只做一件事

我在项目中遇到过这样一个例子:一个叫 OrderProcessor 的类,里面既有订单校验、又有库存扣减、还有发送邮件通知。看起来每个方法都不长,但整个类做了三件完全不同的事。后来我把它拆成了三个类:OrderValidatorInventoryManagerNotificationService。每个类都短了,但更重要的是,每个类的职责清晰了。

核心原则: 一个类应该只有一个改变的理由。如果你能说出"这个类因为A原因要改,也因为B原因要改",那它就该拆。

内聚性原则:方法之间要"抱团"

内聚性,说白了就是:一个类里的方法和变量,应该紧密相关。它们应该为了同一个目标而工作。

举个例子:

// 低内聚:这个类既管用户信息,又管文件上传
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,新同事看到会不会一脸懵?这就是内聚性被破坏了。

避坑指南: 我曾经接手过一个项目,一个类有3000多行,里面既有数据库操作、又有UI渲染、还有网络请求。改一个地方,崩三个功能。后来花了整整两周才拆干净。所以,写代码时多问自己一句:"这个方法真的属于这个类吗?"

将系统构造与使用分离

这是很多架构问题的根源。什么叫"构造"?就是对象的创建、依赖的组装。什么叫"使用"?就是业务逻辑的执行。

很多人喜欢在业务代码里直接 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 包,里面放所有对象的创建逻辑。业务代码只依赖接口,不依赖具体实现。这样换实现的时候,改一行配置就够了。

知识体系总览

下面这张图,把本章的核心逻辑串起来了:

类与系统设计 类的组织 常量 → 变量 → 方法 公共在前,私有在后 类应该短小 不是行数少 而是职责少 内聚性原则 方法变量紧密相关 一个类只做一件事 将系统构造与使用分离 构造(创建对象)→ 交给工厂/DI容器 使用(业务逻辑)→ 只依赖接口

嗯,这四个点其实是一体的。类的组织是"形式",短小是"尺度",内聚是"原则",构造与使用分离是"架构手段"。把它们结合起来,你的代码会变得容易读、容易改、容易测。

一句话总结: 写类就像写文章——段落要短、主题要明、不要在一段里又写天气又写数学。把"怎么造"和"怎么用"分开,代码自然就干净了。

公众号:蓝海资料掘金营,微信deep3321