序章:一座待重建的“烂尾楼”
深夜,我收到一份紧急代码评审请求。同事小李在聊天窗口哀嚎:“哥,这个订单模块我实在改不动了,加个优惠券功能已经卡了三天,一动就崩!”
我打开那个传说中的OrderService.java文件——一个高达1200行的类,没有注释,方法长度普遍超过100行,嵌套的if-else像俄罗斯套娃,业务逻辑和数据库操作、短信发送、日志打印全部搅在一起。更可怕的是,这个类已经被5个不同模块直接调用,任何修改都可能引发连锁反应。
// 原始烂代码示例(简化版)
public class OrderService {
public String createOrder(Map<String, Object> params) {
// 参数校验(60行)
if (params.get("userId") == null) {
log.error("用户ID为空");
sendAlert("用户ID为空");
return "失败";
}
// 业务校验(80行)
User user = userDao.findById((Long)params.get("userId"));
if (user == null) {
// ... 类似逻辑重复十几次
}
// 价格计算(150行)
double price = calculatePrice(params);
// 库存检查(120行)
// 订单创建(100行)
// 支付处理(200行)
// 日志记录(分散在各处)
// 短信通知(穿插在异常处理中)
// ... 总共1200行
}
// 其他15个类似长度的方法
}
这就是典型的“上帝类”反模式——一个类承担了太多职责,成为了系统的“单点故障”。今天,我将带大家见证这座“代码烂尾楼”如何通过设计模式,一步步重建成高内聚、低耦合、易扩展的现代化架构。
第一幕:识别坏味道,制定重构策略
1.1 代码坏味道诊断清单
-
过长的类和方法:一个类1200行,方法平均200行
-
重复代码:参数校验、日志记录、异常处理在各处复制粘贴
-
霰弹式修改:改一处业务逻辑需要修改十几个地方
-
过度耦合:直接依赖DAO、短信服务、日志服务等具体实现
-
条件逻辑爆炸:if-else嵌套超过5层,switch-case处理十几种类型
-
数据泥团:多个方法接收相同的冗长参数列表
1.2 重构策略:分而治之,模式制胜
我们的重构将分四步走,每一步引入1-2个关键设计模式:
-
职责分离:运用策略模式和工厂模式解耦业务逻辑
-
流程标准化:运用模板方法模式和责任链模式规范处理流程
-
对象创建优化:运用建造者模式和原型模式简化对象构建
-
行为扩展:运用观察者模式和装饰器模式实现动态扩展
第二幕:重构实战——从混乱到秩序
2.1 第一步:分离变化点——策略模式 + 工厂模式
问题:价格计算逻辑有6种不同类型(普通、促销、团购、秒杀、会员、拼团),全部挤在一个500行的calculatePrice()方法中。
重构前:
public double calculatePrice(Order order) {
if (order.getType().equals("NORMAL")) {
// 30行普通价格计算
} else if (order.getType().equals("PROMOTION")) {
// 40行促销价格计算
} else if (order.getType().equals("GROUP_BUY")) {
// 50行团购价格计算
}
// ... 另外3种类型
}
运用策略模式重构:
// 1. 定义策略接口
public interface PriceStrategy {
double calculate(Order order);
boolean supports(OrderType type);
}
// 2. 实现具体策略
@Component
public class NormalPriceStrategy implements PriceStrategy {
@Override
public double calculate(Order order) {
// 专注普通价格计算,仅15行
return order.getBasePrice() * order.getQuantity();
}
@Override
public boolean supports(OrderType type) {
return OrderType.NORMAL == type;
}
}
@Component
public class PromotionPriceStrategy implements PriceStrategy {
@Override
public double calculate(Order order) {
// 专注促销计算,仅20行
double base = order.getBasePrice() * 0.9;
if (order.getQuantity() > 10) {
base *= 0.95;
}
return base * order.getQuantity();
}
@Override
public boolean supports(OrderType type) {
return OrderType.PROMOTION == type;
}
}
// 3. 策略工厂
@Component
public class PriceStrategyFactory {
private final Map<OrderType, PriceStrategy> strategyMap = new HashMap<>();
public PriceStrategyFactory(List<PriceStrategy> strategies) {
// Spring会自动注入所有PriceStrategy实现
strategies.forEach(strategy -> {
for (OrderType type : OrderType.values()) {
if (strategy.supports(type)) {
strategyMap.put(type, strategy);
break;
}
}
});
}
public PriceStrategy getStrategy(OrderType type) {
return strategyMap.get(type);
}
}
// 4. 精简后的价格计算服务
@Service
public class PriceCalculationService {
private final PriceStrategyFactory factory;
public double calculatePrice(Order order) {
PriceStrategy strategy = factory.getStrategy(order.getType());
return strategy.calculate(order);
}
}
效果对比:
-
✅ 代码行数:从500+行减少到150行(所有策略类总和)
-
✅ 可维护性:新增价格策略只需新增一个类,无需修改现有代码
-
✅ 可测试性:每个策略可独立单元测试
-
✅ 可读性:每个策略职责单一,逻辑清晰
2.2 第二步:规范处理流程——模板方法模式 + 责任链模式
问题:订单创建流程混乱,校验、计算、保存、通知等步骤随意穿插。
重构前:
public String createOrder(OrderRequest request) {
// 步骤1:校验(但有些校验在中间做)
// 步骤2:计算价格(中间又做了一次库存校验)
// 步骤3:扣库存(但失败后的回滚逻辑不完整)
// 步骤4:保存订单(这里又校验了用户状态)
// 步骤5:发短信(如果失败,订单已保存,状态不一致)
// 步骤混乱,异常处理不一致
}
运用模板方法模式重构:
// 1. 定义订单创建模板
public abstract class AbstractOrderCreator {
// 模板方法:定义算法骨架
public final Order createOrder(OrderRequest request) {
preValidate(request); // 前置校验
Order order = buildOrder(request); // 构建订单
processBusiness(order); // 业务处理
saveOrder(order); // 持久化
postProcess(order); // 后置处理
return order;
}
// 基本步骤,子类必须实现
protected abstract void preValidate(OrderRequest request);
protected abstract Order buildOrder(OrderRequest request);
protected abstract void processBusiness(Order order);
// 可选步骤,提供默认实现
protected void saveOrder(Order order) {
orderRepository.save(order);
}
protected void postProcess(Order order) {
// 默认无后置处理
}
}
// 2. 具体订单创建器
@Service
public class NormalOrderCreator extends AbstractOrderCreator {
@Override
protected void preValidate(OrderRequest request) {
// 普通订单的特定校验
if (request.getUserId() == null) {
throw new ValidationException("用户ID不能为空");
}
// ... 其他校验
}
@Override
protected Order buildOrder(OrderRequest request) {
return Order.builder()
.userId(request.getUserId())
.items(request.getItems())
.build();
}
@Override
protected void processBusiness(Order order) {
// 普通订单的业务处理
inventoryService.deduct(order.getItems());
}
@Override
protected void postProcess(Order order) {
// 普通订单发送确认短信
smsService.sendConfirm(order.getUserPhone());
}
}
// 3. 结合责任链模式处理复杂校验
@Component
public class OrderValidationChain {
private List<Validator> validators;
public ValidationResult validate(OrderRequest request) {
ValidationResult result = new ValidationResult();
for (Validator validator : validators) {
if (!validator.validate(request, result)) {
break; // 任一验证失败则终止
}
}
return result;
}
}
// 校验器接口
public interface Validator {
boolean validate(OrderRequest request, ValidationResult result);
}
// 具体校验器
@Component
@Order(1) // 定义校验顺序
public class UserValidator implements Validator {
@Override
public boolean validate(OrderRequest request, ValidationResult result) {
if (request.getUserId() == null) {
result.addError("用户不存在");
return false;
}
return true;
}
}
效果对比:
-
✅ 流程标准化:所有订单创建遵循相同流程骨架
-
✅ 流程控制:父类控制流程,子类填充细节
-
✅ 异常安全:模板方法可统一添加事务和异常处理
-
✅ 复用性:公共步骤在父类中复用
2.3 第三步:优雅构建对象——建造者模式 + 原型模式
问题:Order对象有20+个字段,构造方法参数列表过长,且不同场景需要不同初始化。
重构前:
// 构造函数灾难
Order order = new Order(userId, items, address, price, discount,
coupon, remark, invoice, delivery, ...);
// 或者使用setter,但可能遗漏必填字段
Order order = new Order();
order.setUserId(userId);
// ...忘记设置其他10个必填字段
运用建造者模式重构:
// 1. 建造者模式实现
public class Order {
private final Long orderId;
private final Long userId;
private final List<OrderItem> items;
private final Address address;
private final BigDecimal totalAmount;
// ... 其他字段
// 私有构造,强制使用建造者
private Order(Builder builder) {
this.orderId = builder.orderId;
this.userId = builder.userId;
this.items = builder.items;
this.address = builder.address;
this.totalAmount = builder.totalAmount;
// ... 其他字段
}
// 静态建造者方法
public static Builder builder() {
return new Builder();
}
// 建造者内部类
public static class Builder {
private Long orderId;
private Long userId;
private List<OrderItem> items = new ArrayList<>();
private Address address;
private BigDecimal totalAmount;
public Builder orderId(Long orderId) {
this.orderId = orderId;
return this;
}
public Builder userId(Long userId) {
this.userId = userId;
return this;
}
// ... 其他字段的构建方法
// 必填字段校验
public Order build() {
if (userId == null) {
throw new IllegalStateException("用户ID不能为空");
}
if (items.isEmpty()) {
throw new IllegalStateException("订单项不能为空");
}
return new Order(this);
}
}
}
// 2. 流畅的API使用
Order order = Order.builder()
.userId(123L)
.items(items)
.address(address)
.totalAmount(calculateTotal(items))
.build();
// 3. 结合原型模式快速克隆
public class Order implements Cloneable {
// ... 其他代码
@Override
public Order clone() {
try {
Order cloned = (Order) super.clone();
// 深拷贝可变对象
cloned.items = new ArrayList<>(this.items);
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
// 创建相似订单
Order templateOrder = getOrderTemplate();
Order newOrder = templateOrder.clone()
.toBuilder() // 假设有toBuilder方法
.orderId(generateId())
.createdTime(new Date())
.build();
2.4 第四步:动态扩展行为——观察者模式 + 装饰器模式
问题:订单状态变更时需要通知多个系统,硬编码导致耦合。
重构前:
public void updateOrderStatus(Long orderId, OrderStatus status) {
orderDao.updateStatus(orderId, status);
// 硬编码的通知逻辑
if (status == OrderStatus.PAID) {
smsService.sendPaidNotification(orderId);
emailService.sendReceipt(orderId);
couponService.updateCouponStatus(orderId);
inventoryService.updateInventory(orderId);
// 每加一个通知点都要改这里
}
}
运用观察者模式重构:
// 1. 定义事件
public class OrderStatusChangedEvent {
private final Long orderId;
private final OrderStatus oldStatus;
private final OrderStatus newStatus;
private final LocalDateTime changeTime;
// 建造者省略...
}
// 2. 事件发布者
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public void updateOrderStatus(Long orderId, OrderStatus newStatus) {
OrderStatus oldStatus = orderDao.getStatus(orderId);
orderDao.updateStatus(orderId, newStatus);
// 发布事件,而不是直接调用
eventPublisher.publishEvent(new OrderStatusChangedEvent(
orderId, oldStatus, newStatus, LocalDateTime.now()
));
}
}
// 3. 事件监听器
@Component
@Slf4j
public class OrderStatusChangeListeners {
@EventListener
@Async
public void handlePaidEvent(OrderStatusChangedEvent event) {
if (event.getNewStatus() == OrderStatus.PAID) {
// 发送短信通知
smsService.sendPaymentSuccess(event.getOrderId());
}
}
@EventListener
@Order(1) // 处理顺序
public void handleShippedEvent(OrderStatusChangedEvent event) {
if (event.getNewStatus() == OrderStatus.SHIPPED) {
// 更新库存
inventoryService.confirmDeduction(event.getOrderId());
}
}
// 新增监听器只需添加新方法,无需修改原有代码
@EventListener
public void handleCompletedEvent(OrderStatusChangedEvent event) {
if (event.getNewStatus() == OrderStatus.COMPLETED) {
// 新增:订单完成时给用户加积分
pointService.addPoints(event.getOrderId());
}
}
}
运用装饰器模式增强功能:
// 1. 原始订单服务接口
public interface OrderService {
Order createOrder(OrderRequest request);
Order getOrder(Long orderId);
}
// 2. 基础实现
@Service
public class BasicOrderService implements OrderService {
@Override
public Order createOrder(OrderRequest request) {
// 基础创建逻辑
return orderRepository.save(convertToOrder(request));
}
@Override
public Order getOrder(Long orderId) {
return orderRepository.findById(orderId).orElse(null);
}
}
// 3. 装饰器:添加缓存功能
@Service
@Primary // 让Spring优先注入这个装饰后的版本
public class CachedOrderService implements OrderService {
private final OrderService delegate;
private final CacheManager cacheManager;
public CachedOrderService(BasicOrderService delegate, CacheManager cacheManager) {
this.delegate = delegate;
this.cacheManager = cacheManager;
}
@Override
public Order createOrder(OrderRequest request) {
Order order = delegate.createOrder(request);
// 创建后清除相关缓存
cacheManager.evict("orders", order.getUserId().toString());
return order;
}
@Override
@Cacheable(value = "orders", key = "#orderId")
public Order getOrder(Long orderId) {
// 缓存装饰:先查缓存,没有再查数据库
return delegate.getOrder(orderId);
}
}
// 4. 可以继续装饰:添加日志、监控、限流等
@Service
public class MonitoredOrderService implements OrderService {
private final OrderService delegate;
private final MeterRegistry meterRegistry;
@Override
public Order createOrder(OrderRequest request) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
return delegate.createOrder(request);
} finally {
sample.stop(Timer.builder("order.create.time")
.register(meterRegistry));
}
}
}
第三幕:重构成果与架构对比
3.1 重构前后架构对比图
重构前(单体架构):
┌─────────────────────────────────────┐
│ OrderService (1200行) │
│ ├─ createOrder() 350行 │
│ ├─ calculatePrice() 500行 │
│ ├─ updateStatus() 200行 │
│ └─ 其他10+个方法 │
│ 直接依赖:UserDao, ProductDao, │
│ SmsService, EmailService... │
└─────────────────────────────────────┘
重构后(领域驱动+模式组合):
┌─────────────────────────────────────────────┐
│ OrderFacade (门面) │
├─────────────────────────────────────────────┤
│ OrderService (接口) │
│ ├─ CachedOrderService (装饰器) │
│ │ └─ MonitoredOrderService (装饰器) │
│ │ └─ BasicOrderService (实现) │
├─────────────────────────────────────────────┤
│ OrderFactory (工厂) │
│ ├─ NormalOrderCreator (模板方法) │
│ ├─ GroupOrderCreator (模板方法) │
│ └─ 其他订单创建器 │
├─────────────────────────────────────────────┤
│ PriceStrategyFactory (策略工厂) │
│ ├─ NormalPriceStrategy (策略) │
│ ├─ PromotionPriceStrategy (策略) │
│ └─ 其他价格策略 │
├─────────────────────────────────────────────┤
│ OrderValidationChain (责任链) │
│ ├─ UserValidator │
│ ├─ InventoryValidator │
│ └─ 其他校验器 │
├─────────────────────────────────────────────┤
│ EventPublisher (观察者模式) │
│ └─ 多个EventListener │
└─────────────────────────────────────────────┘
3.2 量化改进指标
| 指标 | 重构前 | 重构后 | 改进率 |
|---|---|---|---|
| 单类最大行数 | 1200行 | 150行 | -87.5% |
| 方法平均行数 | 85行 | 25行 | -70.6% |
| 单元测试覆盖率 | 15% | 85% | +466% |
| 新增功能平均耗时 | 2-3天 | 2-3小时 | -90% |
| 认知复杂度 | 极高 | 中等 | -60% |
| 圈复杂度 | 45 | 8 | -82% |
3.3 真实业务场景:添加优惠券功能
重构前(小李的噩梦):
// 需要修改OrderService的多个方法,涉及300+行代码
public String createOrder(OrderRequest request) {
// ... 原有逻辑
// 新增:优惠券处理(混杂在原有逻辑中)
if (request.getCouponCode() != null) {
Coupon coupon = couponDao.findByCode(request.getCouponCode());
if (coupon == null) {
log.error("优惠券不存在");
// 但订单可能已经部分处理,需要回滚...
}
// 优惠券使用逻辑(50行)
// 与价格计算逻辑耦合
// 需要修改calculatePrice方法
}
// ... 更多修改
}
重构后(优雅扩展):
// 1. 新增优惠券策略(仅需新增一个类)
@Component
public class CouponPriceStrategy implements PriceStrategy {
private final CouponService couponService;
@Override
public double calculate(Order order) {
BigDecimal discount = couponService.calculateDiscount(order);
return order.getBaseAmount() - discount.doubleValue();
}
@Override
public boolean supports(OrderType type) {
return OrderType.COUPON == type;
}
}
// 2. 新增优惠券校验器
@Component
@Order(3)
public class CouponValidator implements Validator {
@Override
public boolean validate(OrderRequest request, ValidationResult result) {
if (request.getCouponCode() != null) {
Coupon coupon = couponService.validateCoupon(
request.getCouponCode(), request.getUserId()
);
if (coupon == null) {
result.addError("优惠券无效");
return false;
}
}
return true;
}
}
// 3. 优惠券使用监听器
@Component
public class CouponUsageListener {
@EventListener
public void onOrderPaid(OrderStatusChangedEvent event) {
if (event.getNewStatus() == OrderStatus.PAID) {
couponService.markAsUsed(event.getOrderId());
}
}
}
// 完成!无需修改任何现有类
第四幕:重构心法与模式选择指南
4.1 设计模式选用决策树
遇到问题 → 判断类型 → 选择模式
│
├─ 对象创建复杂? → 建造者/工厂/原型
│
├─ 多个if-else判断类型? → 策略/状态
│
├─ 流程固定但步骤可变? → 模板方法
│
├─ 需要按顺序处理? → 责任链
│
├─ 一变化需要通知多方? → 观察者
│
├─ 需要动态添加功能? → 装饰器
│
└─ 简化复杂子系统访问? → 门面
4.2 重构黄金法则
-
小步快跑:每次重构不超过30分钟,频繁提交
-
测试守护:重构前先写测试,确保行为不变
-
模式不滥用:不是每个问题都需要模式,KISS原则优先
-
持续演进:架构不是一次设计出来的,而是演进出来的
4.3 常见反模式与纠正
-
上帝类 → 单一职责原则 + 策略/工厂模式
-
重复代码 → 模板方法/继承/组合
-
霰弹式修改 → 观察者/事件驱动
-
过度耦合 → 依赖注入 + 接口隔离
-
条件爆炸 → 策略/状态/表驱动
终章:从技工到工匠的蜕变
三个月后,小李兴奋地告诉我:“哥,昨天产品经理临时加需求——支持七天无理由退货的逆向订单流程,我只用了2小时就上线了!就新增了一个RefundOrderCreator和几个策略类,完全没动老代码!”
这就是设计模式的魅力所在。它不只是解决眼前问题的工具,更是预防未来问题的智慧。通过这次重构,我们收获的不仅是:
-
可维护的代码:新人能在一天内理解模块
-
可扩展的架构:新需求平均开发时间减少70%
-
可靠的质量:bug率下降85%,测试覆盖率85%+
-
可复用的资产:价格策略、校验链等可复用于其他模块
但更重要的是,团队建立了对代码质量的信仰。我们开始主动识别坏味道,小步重构,持续改进。代码不再是负担,而是值得骄傲的资产。
烂代码是负债,好代码是资产。设计模式就是帮你将技术负债转为技术资产的最佳杠杆。下一次当你面对一团乱麻的代码时,不要绝望——拿起设计模式这把手术刀,勇敢地开始重构吧。
毕竟,每个优雅的系统,都曾是一团糟的代码。区别在于,有人选择了忍受,而有人选择了改变。

被折叠的 条评论
为什么被折叠?



