Java面向对象思想解析:如何编写出高质量的代码
发布时间:2025-11-01 00:13       
以下内容落地、务实、可直接复用;目标是把“面向对象”从口号变成可交付的工程实践。🙂
一、核心结论(直说)
- 以领域建模驱动代码结构:先讲业务语言,再写类;拒绝“表结构即模型”的贫血对象。
- 高内聚、低耦合:类只做一件事,把变化点关进小房间。
- 显式不变式:对象创建即校验,非法状态不落地。
- 组合优先于继承:继承只用于“是一个”,其余用组合与委托。
- 接口编程:依赖抽象而不是具体实现,方便替换与测试。
- 面向用例而非层堆砌:以“应用服务 → 领域模型 → 基础设施”的最小骨架交付价值。
二、设计基准(可量化)
| 维度 | 目标 | 快速判断 |
|---|---|---|
| 内聚 | 单一职责 | 类名能否一句话说清?方法是否超过7±2? |
| 耦合 | 依赖倒置 | 业务不直接依赖外部SDK;通过接口/端口适配 |
| 可测性 | 单测先行 | 领域层无需Spring环境即可测 |
| 可演进 | 封装变化 | 第三方依赖、规则频繁变动处做“防腐层” |
| 可读性 | 语义清晰 | 用领域词汇命名,拒绝Manager/Util套娃 |
三、建模抓手(DDD最小落地包)
- 实体(Entity):有标识、会变;承担领域行为与不变式。
- 值对象(Value Object):无标识、不可变;用于表达概念与校验。
- 聚合(Aggregate):一致性边界;对外只暴露聚合根。
- 领域服务:跨实体的业务规则;无状态。
- 应用服务:编排用例;不写业务规则。
四、代码示例(Java 21+,利用 record/sealed/Pattern Matching)
// 值对象:不可变,创建即校验
public record Money(long cents) {
public Money {
if (cents < 0) throw new IllegalArgumentException("金额不能为负");
}
public Money add(Money other){ return new Money(this.cents + other.cents); }
public boolean gte(Money other){ return this.cents >= other.cents; }
}
// 聚合根:封装不变式与行为
public final class Order {
private final String id;
private final List<OrderLine> lines = new ArrayList<>();
private OrderStatus status = OrderStatus.CREATED;
public Order(String id){ this.id = Objects.requireNonNull(id); }
public void addLine(Product product, int qty){
if (status != OrderStatus.CREATED) throw new IllegalStateException("非创建态不可加行");
lines.add(new OrderLine(product, qty));
}
public Money total(){
return lines.stream()
.map(OrderLine::subtotal)
.reduce(new Money(0), Money::add);
}
public void pay(Money paid){
if (!paid.gte(total())) throw new IllegalArgumentException("实付不足");
this.status = OrderStatus.PAID;
}
}
// 组合优先:行项目组合值对象
record OrderLine(Product product, int qty){
OrderLine {
if (qty <= 0) throw new IllegalArgumentException("数量必须大于0");
}
Money subtotal(){ return new Money(product.price().cents() * qty); }
}
record Product(String id, Money price){}
enum OrderStatus { CREATED, PAID }
要点:
- 用
record表达不可变值对象,天然线程安全。 - 不变式写在构造器/工厂方法中;拒绝“先new,后校验”的脆弱流程。
- 领域行为放进聚合根(如
pay),避免贫血模型。
五、演进策略(把变化关进小房间)
- 接口 + 适配器:外部支付、存储、消息等,经由端口接口隔离;替换实现时应用层无感。
- 策略模式:计价、风控、路由等规则高频变更点抽象为策略集合。
- 防腐层:与遗留系统或三方SDK交互,做字段、异常、语义的双向转换。
- 可观察性:领域事件 + 结构化日志,问题可回放。📈
六、代码评审清单(PR前自检)
- 是否存在无校验的setters?(有就收口到构造/方法)
- 是否把“用例编排”和“领域规则”混在Controller里?(拆)
- 命名是否使用领域语言?(用户能读懂)
- 是否为边界条件写了单测?(空集合、极值、并发)
- 是否出现过度继承?能否改为组合?
七、最小流程图(从用例到交付)
graph LR
A[接收用例请求]-->B[应用服务编排]
B-->C[加载聚合根]
C-->D[执行领域行为:校验+状态变更]
D-->E[发布领域事件]
E-->F[持久化/外部依赖经端口适配器]
F-->G[返回结果与审计日志]
八、实践tips(真刀真枪)
- 虚拟线程(Java 21):I/O密集型应用显著提升吞吐,但领域模型不应泄露线程细节。
- Pattern Matching for switch:简化业务规则分派,配合策略更干净。
- Optional/Null Object:消灭NPE,语义明确。
- 记录型事件:领域事件用
record,天然不可变,序列化友好。🚀
一句话收尾:用面向对象把“业务词汇”铸进代码,把变化隔离在边界,把规则写进对象——你的系统会更稳、更易测、更能迭代。