综合实战:用设计模式重构遗留电商订单系统
说实话,我接手过不少遗留系统。每次打开那些几千行的「上帝类」,心里都咯噔一下。今天要聊的这个电商订单系统,就是典型代表——一个 OrderService 类,足足 3000 行,if-else 嵌套了 8 层。嗯,是时候用设计模式给它「刮骨疗毒」了。
系统现状:一团乱麻
先说说这个系统长什么样。它处理订单的完整生命周期:创建、支付、发货、确认收货、退款、取消。每个环节都有不同的业务逻辑,而且还在不断叠加新需求。
我接手时,代码大概是这样的:
public class OrderService {
public void processOrder(Order order, String action) {
if ("CREATE".equals(action)) {
// 50行创建逻辑
} else if ("PAY".equals(action)) {
// 80行支付逻辑
} else if ("SHIP".equals(action)) {
// 60行发货逻辑
}
// 还有十几个 else if...
}
}
你想想看,每次加一个新动作,就得往这个类里塞代码。测试越来越慢,改一个地方可能影响十个功能。这哪是维护代码,分明是在拆炸弹。
重构目标:让代码「活」起来
我的目标很明确:每个模式解决一类问题,让代码可扩展、可测试、可维护。具体来说,我打算用这些模式:
| 设计模式 | 解决的问题 | 应用场景 |
|---|---|---|
| 单例模式 | 全局唯一实例 | 配置管理器、日志记录器 |
| 工厂模式 | 对象创建逻辑 | 订单处理器创建 |
| 策略模式 | 算法族封装 | 支付方式、配送方式 |
| 状态模式 | 状态流转 | 订单状态机 |
| 观察者模式 | 事件通知 | 订单状态变更通知 |
| 代理模式 | 访问控制 | 权限校验、日志记录 |
第一步:用单例模式管理配置
我先从最简单的开始。系统里有个配置管理器,负责读取订单相关的配置参数。以前每次用到配置都 new 一个实例,数据库连接开了又关,关了又开。我习惯用枚举实现单例,简洁又线程安全。
public enum OrderConfigManager {
INSTANCE;
private Properties props;
OrderConfigManager() {
props = new Properties();
// 加载配置文件
loadConfig();
}
public String getConfig(String key) {
return props.getProperty(key);
}
}
这样写的好处是:全局只有一个配置实例,不会重复加载。我在项目中遇到过因为配置重复加载导致的性能问题,后来用单例模式一把就解决了。
第二步:用工厂模式创建订单处理器
接下来处理那个巨无霸的 if-else。我决定把每个订单动作拆成独立的处理器,然后用工厂来创建它们。
// 策略接口
public interface OrderHandler {
void handle(Order order);
}
// 具体处理器
public class CreateOrderHandler implements OrderHandler {
public void handle(Order order) {
// 创建订单逻辑
}
}
public class PayOrderHandler implements OrderHandler {
public void handle(Order order) {
// 支付逻辑
}
}
// 工厂类
public class OrderHandlerFactory {
private static Map<String, OrderHandler> handlers = new HashMap<>();
static {
handlers.put("CREATE", new CreateOrderHandler());
handlers.put("PAY", new PayOrderHandler());
// 注册更多处理器
}
public static OrderHandler getHandler(String action) {
return handlers.get(action);
}
}
这样一来,主流程就变成了:
OrderHandler handler = OrderHandlerFactory.getHandler(action);
handler.handle(order);
干净利落。加新动作?只需要新增一个处理器类,然后在工厂里注册一下。不用改任何现有代码。
第三步:用状态模式管理订单状态机
订单状态流转是这个系统最复杂的部分。以前的状态判断散落在各个方法里,经常出现「订单已发货还能取消」这种 bug。我决定用状态模式来重构。
先画个状态图:
状态模式的精髓在于:每个状态都是一个独立类,状态之间的转换由状态类自己决定。
// 状态接口
public interface OrderState {
void pay(OrderContext context);
void ship(OrderContext context);
void confirm(OrderContext context);
void cancel(OrderContext context);
}
// 具体状态:待支付
public class PendingPaymentState implements OrderState {
public void pay(OrderContext context) {
System.out.println("支付成功");
context.setState(new PaidState());
}
public void ship(OrderContext context) {
throw new IllegalStateException("未支付不能发货");
}
public void confirm(OrderContext context) {
throw new IllegalStateException("未支付不能确认");
}
public void cancel(OrderContext context) {
System.out.println("取消订单");
context.setState(new CancelledState());
}
}
// 上下文
public class OrderContext {
private OrderState state;
public OrderContext() {
this.state = new PendingPaymentState();
}
public void setState(OrderState state) {
this.state = state;
}
public void pay() { state.pay(this); }
public void ship() { state.ship(this); }
public void confirm() { state.confirm(this); }
public void cancel() { state.cancel(this); }
}
第四步:用观察者模式处理事件通知
订单状态变了,需要通知库存系统、物流系统、用户通知系统。以前的做法是在每个状态变更的地方硬编码调用。我改用观察者模式,让订单状态变更时自动通知所有订阅者。
// 事件接口
public interface OrderEvent {
String getType();
Order getOrder();
}
// 观察者接口
public interface OrderObserver {
void onEvent(OrderEvent event);
}
// 被观察者
public class OrderEventPublisher {
private List<OrderObserver> observers = new ArrayList<>();
public void subscribe(OrderObserver observer) {
observers.add(observer);
}
public void publish(OrderEvent event) {
for (OrderObserver observer : observers) {
observer.onEvent(event);
}
}
}
// 具体观察者
public class InventoryObserver implements OrderObserver {
public void onEvent(OrderEvent event) {
if ("PAY".equals(event.getType())) {
// 扣减库存
}
}
}
public class NotificationObserver implements OrderObserver {
public void onEvent(OrderEvent event) {
// 发送通知
}
}
这样设计的好处是:订单模块不需要知道谁在监听,新增通知渠道也不用改订单代码。我在项目中用这种方式对接过短信、邮件、站内信三个通知渠道,后来加微信通知,一行订单代码都没改。
第五步:用代理模式做权限控制
最后,有些操作需要权限校验。比如只有管理员才能取消已发货的订单。我不想把权限逻辑混进业务代码里,于是用代理模式包了一层。
public class OrderServiceProxy implements OrderService {
private OrderService realService;
private UserContext userContext;
public OrderServiceProxy(OrderService realService, UserContext userContext) {
this.realService = realService;
this.userContext = userContext;
}
public void cancelOrder(Order order) {
if (!userContext.isAdmin()) {
throw new SecurityException("只有管理员才能取消订单");
}
realService.cancelOrder(order);
}
}
代理模式说白了就是「中间人」。它可以在不改变原有代码的情况下,增加额外的控制逻辑。我习惯用它来做:权限校验、日志记录、性能监控。
重构后的架构总览
来,看看重构后的整体架构:
重构效果:数字说话
重构完成后,我统计了一下变化:
- 代码行数:从 3000 行降到 1200 行(减少了 60%)
- 类数量:从 5 个增加到 18 个(每个类职责更单一)
- 测试覆盖率:从 20% 提升到 85%
- 新增功能耗时:从 3 天缩短到 0.5 天
核心收获:设计模式不是银弹,但它是解决特定问题的「最佳实践」。这次重构让我深刻体会到:好的架构是「生长」出来的,不是「设计」出来的。先让代码跑起来,再逐步用模式优化,这才是务实的做法。
说实话,重构遗留系统就像给飞行中的飞机换引擎。但只要你选对了模式,每一步都稳扎稳打,结果会让你惊喜。嗯,这就是我这次实战的全部心得了。
公众号:蓝海资料掘金营,微信deep3321