类设计应明确职责边界与协作关系,避免贫血模型和滥用继承,优先组合与接口,建模先于框架,确保状态行为内聚、领域逻辑清晰。

类设计不是堆砌功能,而是表达清晰的职责边界和合理的协作关系。Java中很多“看似能跑”的类,后期维护成本高、扩展困难,往往源于建模阶段的几个关键疏忽。
职责单一,但别“单薄”
一个类应该只做一件事,但这件事要真正闭环——比如red">OrderService不该只负责调用DAO,而应封装订单创建的完整规则(库存校验、价格计算、状态流转);反过来,把“发短信”“写日志”“更新缓存”全塞进同一个方法,就违背了单一职责。
- 判断标准:如果抽掉某段逻辑,这个类还能独立完成它的核心语义吗?
- 常见反例:DTO里加validate()方法、Entity里写SQL拼接逻辑、Controller里处理业务规则
- 建议:用领域服务(Domain Service)或策略类(Strategy)剥离可变行为,让实体聚焦数据与内聚行为
继承慎用,优先组合与接口
Java里extends容易滥用,尤其在“is-a”关系不稳固时。比如“Bird extends Flyable”看似合理,但鸵鸟是Bird却不Flyable;而用接口+组合(如Bird has-a FlyingBehavior)更灵活、更易测试。
- 继承适合描述稳定、不可逆的分类体系(如IOException extends Exception)
- 一旦子类需要覆盖父类多个方法、或出现“菱形继承”倾向(如A→B、A→C、B&C都想复用D),说明该用接口或委托
- 工具类(Utils)和模板方法(Template Method)是继承的合理场景,但需确保钩子方法(hook method)语义明确、调用时机可控
状态与行为尽量内聚,避免贫血模型
只含getter/setter的POJO + 大量静态工具方法,是典型的贫血模型。它把业务逻辑散落在各处,导致状态变更不可控、事务边界模糊、难以保证一致性。
立即学习“Java免费学习笔记(深入)”;
- 例如:账户余额修改,应在Account类内部校验透支、更新余额、记录流水,而不是由外部Service反复读-改-写
- 关键动作建议封装为有副作用的方法(如account.withdraw(amount)),而非暴露setBalance()
- 对不可变状态(如ID、创建时间),用final字段+构造器初始化,比靠约定“不修改”更可靠
建模先于框架,别被Spring惯坏
很多类一上来就加@Service@Component,字段直接@Autowired,结果领域对象依赖了Spring上下文、事务注解侵入业务逻辑、测试必须启容器。
- 正确顺序:先画类图、定义方法签名、明确输入输出和异常路径,再考虑怎么注入、怎么持久化
- Repository接口应面向领域对象设计(如findActiveOrdersByCustomer(CustomerId)),而不是迁就JPA的findByStatusAndCustomerId()
- 框架代码(如Controller、Config、Adapter)和领域代码(Entity、ValueObject、DomainService)必须物理隔离,包结构体现分层意图
基本上就这些。类不是语法容器,它是团队沟通的契约,也是系统演化的锚点。建模时多问一句“这个类在业务里代表什么?谁会用它?它变化时会影响谁?”,比纠结用不用Lombok实在得多。










