类设计应先确认概念是否为业务中独立有边界的实体,避免Helper/Util等职责不清命名;优先用领域名词建类、动词作方法,拒绝Manager/Handler泛滥;修改频繁则检查职责是否单一;继承需满足“是一种”语义,否则用组合;接口应聚焦同一组能力,命名体现契约而非实现;封装重在控制可变性而非仅设private。

类的设计要先想清楚“它该不该存在”
面向对象不是一上来就建类,而是先问:这个概念在业务里是不是一个独立的、有明确边界的实体?比如 Order 可以,但 OrderHelper 或 OrderUtil 往往是设计信号不良——说明职责还没收敛到合适的类里。
常见错误是把过程式逻辑硬塞进类:写个 OrderProcessor,里面堆满 validate()、calculate()、sendNotification(),却让 Order 只剩几个 getXXX()。结果是行为和数据分离,修改一个校验规则要跳 3 个类。
实操建议:
- 从领域名词出发建类(
User、Payment、Inventory),动词优先落在对应类的方法里(user.activate()、payment.refund()) - 拒绝“Manager”“Handler”“Service”后缀泛滥;如果非用,确保它封装的是跨领域协调逻辑,而非单个实体的操作
- 一个类修改频繁?检查它是否承担了多个变化原因——可能是职责没拆开,比如把库存扣减和日志记录混在同一个
InventoryService里
继承不是为了复用代码,而是为了表达“是一种”
extends 的滥用是 Java 项目里最典型的 OOP 误用。看到两个类有相似字段和方法,第一反应不是拉个父类,而是先问:子类在语义上真的是父类的一种吗?Dog extends Animal 合理,ReportExporter extends DatabaseConnection 就荒谬——后者只是用了连接,不是“是一种连接”。
立即学习“Java免费学习笔记(深入)”;
容易踩的坑:
- 为复用而继承,导致子类被迫继承无意义的父类方法(比如
DatabaseConnection.close()被ReportExporter继承后根本不用,还可能被误调) - 父类加个新方法或改签名,所有子类被动受影响,破坏封装
- Java 单继承限制下,过度依赖继承会堵死后续扩展路径
更稳妥的做法是组合:ReportExporter 持有 DatabaseConnection 实例,需要时调用;复用靠接口 + 委托,而不是靠 extends。
接口定义要聚焦“能做什么”,而不是“谁来做”
接口名别叫 OrderServiceImpl,那是实现类的名字;应该叫 OrderRepository 或 OrderValidator——前者声明“能查订单”,后者声明“能验订单”,不暴露实现细节。
关键判断点:
- 接口方法是否都围绕同一组业务能力?如果一个接口既有
save()又有sendEmail(),大概率职责混杂 - 接口是否容易被 mock?如果
OrderService里塞了太多外部依赖(HTTP 调用、文件读写),单元测试就难写,说明它抽象层次错了 - 参数和返回值是否用领域类型而非原始类型?
create(OrderRequest request)比create(String orderId, BigDecimal amount, String userId)更易维护、更可读
一个简单验证法:把接口所有方法列出来,用自然语言连成一句话。如果说不通(比如“一个订单服务能保存、能发邮件、能生成 PDF、能连 Redis”),那就该拆。
封装不是“全 private + getter/setter”,而是控制可变性
给每个字段配 getXXX()/setXXX() 是最省事的封装,也是最无效的封装。真正重要的不是“能不能访问”,而是“能不能随意改”。比如 Order.status 如果允许任意设为 "PAID"、"SHIPPED"、"CANCELLED",却不校验状态流转规则(不能从 "SHIPPED" 直接回退到 "PAID"),那 private 和 public 没本质区别。
实操要点:
- 字段尽量
final,构造时赋值;必须可变的,通过受控方法修改(order.cancel()内部校验再改状态) - 避免暴露集合引用:
getItems()返回Collections.unmodifiableList(items),而不是直接返回原始List - getter 不一定需要——如果外部只读某字段,且该字段不参与任何业务逻辑,才考虑暴露;否则优先提供行为方法(
order.hasOverduePayments()比order.getPayments().stream().anyMatch(...)更安全)
面向对象的落地难点不在语法,而在每次写 class、extends、interface 时,多停半秒想清楚:这个结构是否真实反映了问题域里的关系?还是仅仅让编译通过了?










