25、设计模式综合实战一:基于Spring Boot实现电商订单系统(单例+工厂+策略+观察者)

终于到了综合实战环节。说实话,前面讲了那么多设计模式,都是一个个孤立的点。今天咱们把它们串起来,做一个真正能跑起来的电商订单系统。我选Spring Boot作为基础框架,因为现在大部分Java后端项目都在用这个。

这个案例我会把单例、工厂、策略、观察者四个模式揉在一起。你想想看,一个订单从创建到完成,中间要经历多少步骤?状态变化、支付方式、通知推送……每个环节都有设计模式的身影。

核心思路: 用单例管理全局配置,用工厂创建订单处理器,用策略处理不同支付方式,用观察者监听订单状态变化。

25.1 系统整体架构

先看整体结构。我画了一张图,把四个模式的关系理清楚。这张图我改了三版,最后定下来这个版本——简洁、清晰、不绕弯子。

电商订单系统 - 设计模式架构图 订单请求入口 工厂模式:创建订单处理器 策略模式:支付宝 / 微信 / 银行卡支付 观察者模式:短信 / 邮件 / 日志 / 库存更新 单例:配置管理 入口 工厂 策略 观察者 单例

看到没?单例模式贯穿全局,工厂负责创建,策略处理支付,观察者处理后续通知。每个模式各司其职,互不干扰。

25.2 单例模式:全局配置管理

先讲单例。为什么第一个实现它?因为配置信息到处都要用。支付接口的URL、短信网关的密钥、库存系统的地址……这些信息如果每个类都自己存一份,改起来就麻烦了。

我个人习惯用枚举实现单例。为什么?因为枚举天然防反射攻击、防序列化破坏。你想想看,如果用双重检查锁,还得写volatile,一不小心就踩坑。

public enum OrderConfigSingleton {
    INSTANCE;

    private final Properties properties = new Properties();

    OrderConfigSingleton() {
        // 加载配置文件,只加载一次
        try (InputStream is = getClass().getClassLoader()
                .getResourceAsStream("order-config.properties")) {
            properties.load(is);
        } catch (IOException e) {
            // 实际项目中应该用日志框架
            System.err.println("加载配置失败: " + e.getMessage());
        }
    }

    public String getPaymentGatewayUrl() {
        return properties.getProperty("payment.gateway.url");
    }

    public String getSmsApiKey() {
        return properties.getProperty("sms.api.key");
    }

    public int getOrderTimeoutSeconds() {
        return Integer.parseInt(properties.getProperty("order.timeout.seconds", "1800"));
    }
}
避坑指南: 我曾经在项目里用双重检查锁实现单例,结果忘了加volatile关键字。在高并发下,某个线程读到了未完全初始化的实例,导致配置全部为null。那次排查花了我整整一个下午。从那以后,我写单例只用枚举。

25.3 工厂模式:创建订单处理器

订单类型有很多种:普通订单、秒杀订单、团购订单、预售订单……每种订单的处理逻辑都不一样。如果直接在代码里写if-else,那维护起来就是噩梦。

工厂模式就是干这个的。它把「创建对象」的逻辑集中管理,调用方只需要告诉工厂「我要什么类型」,工厂就返回对应的处理器。

// 订单处理器接口
public interface OrderHandler {
    void handle(Order order);
}

// 普通订单处理器
@Component("normalOrderHandler")
public class NormalOrderHandler implements OrderHandler {
    @Override
    public void handle(Order order) {
        System.out.println("处理普通订单: " + order.getOrderId());
        // 正常库存扣减、价格计算等
    }
}

// 秒杀订单处理器
@Component("flashOrderHandler")
public class FlashOrderHandler implements OrderHandler {
    @Override
    public void handle(Order order) {
        System.out.println("处理秒杀订单: " + order.getOrderId());
        // 秒杀特有的限流、库存校验
    }
}

// 工厂类
@Component
public class OrderHandlerFactory {
    @Autowired
    private Map<String, OrderHandler> handlerMap;

    public OrderHandler getHandler(String orderType) {
        OrderHandler handler = handlerMap.get(orderType + "OrderHandler");
        if (handler == null) {
            throw new IllegalArgumentException("不支持的订单类型: " + orderType);
        }
        return handler;
    }
}

这里我用Spring的依赖注入自动收集所有OrderHandler的实现。你想想看,以后新增一种订单类型,只需要写一个新的Handler类,加上@Component注解,工厂自动就能找到它。完全不用改工厂代码——这就是开闭原则。

25.4 策略模式:多种支付方式

支付方式就更典型了。支付宝、微信支付、银行卡、余额支付……每种支付方式的接口、签名算法、回调处理都不一样。策略模式就是为这种场景量身定做的。

// 支付策略接口
public interface PaymentStrategy {
    boolean pay(Order order, double amount);
    String getChannel();
}

// 支付宝支付
@Component
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public boolean pay(Order order, double amount) {
        System.out.println("支付宝支付: " + amount + " 元");
        // 调用支付宝SDK
        return true;
    }

    @Override
    public String getChannel() {
        return "alipay";
    }
}

// 微信支付
@Component
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public boolean pay(Order order, double amount) {
        System.out.println("微信支付: " + amount + " 元");
        // 调用微信支付SDK
        return true;
    }

    @Override
    public String getChannel() {
        return "wechat";
    }
}

// 支付上下文
@Component
public class PaymentContext {
    @Autowired
    private Map<String, PaymentStrategy> strategyMap;

    public boolean executePayment(Order order, String channel) {
        PaymentStrategy strategy = strategyMap.get(channel);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付渠道: " + channel);
        }
        return strategy.pay(order, order.getTotalAmount());
    }
}
注意: 策略模式的关键在于「策略的切换对调用方透明」。调用方只需要知道支付渠道名称,不需要知道具体实现。我曾经见过一个项目,在支付代码里写了十几个if-else,每次新增支付方式都要改核心逻辑,测试回归量巨大。用策略模式之后,新增支付方式只需要加一个类,测试也只需要测新类。

25.5 观察者模式:订单状态通知

订单支付成功后,要做什么?发短信通知用户、发邮件给商家、更新库存、记录日志、推送消息给运营后台……这些操作如果都写在支付成功的代码里,那支付方法就会变得臃肿不堪。

观察者模式就是解决这个问题的。支付成功后,只发布一个「订单支付成功」事件,所有关心这个事件的观察者自己去处理。

// 事件对象
public class OrderEvent {
    private Order order;
    private String eventType; // PAY_SUCCESS, SHIPPED, DELIVERED 等

    // getter/setter 省略
}

// 事件发布器
@Component
public class OrderEventPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void publishOrderEvent(Order order, String eventType) {
        OrderEvent event = new OrderEvent();
        event.setOrder(order);
        event.setEventType(eventType);
        publisher.publishEvent(event);
    }
}

// 短信观察者
@Component
public class SmsObserver {
    @EventListener
    public void handleOrderEvent(OrderEvent event) {
        if ("PAY_SUCCESS".equals(event.getEventType())) {
            System.out.println("发送短信通知用户: 订单 " 
                + event.getOrder().getOrderId() + " 支付成功");
        }
    }
}

// 库存观察者
@Component
public class InventoryObserver {
    @EventListener
    public void handleOrderEvent(OrderEvent event) {
        if ("PAY_SUCCESS".equals(event.getEventType())) {
            System.out.println("扣减库存: 订单 " 
                + event.getOrder().getOrderId());
        }
    }
}

// 日志观察者
@Component
public class LogObserver {
    @EventListener
    public void handleOrderEvent(OrderEvent event) {
        System.out.println("记录日志: 订单 " 
            + event.getOrder().getOrderId() 
            + " 状态变更 -> " + event.getEventType());
    }
}

这里我用的是Spring的@EventListener注解,底层就是观察者模式。你想想看,如果以后要增加一个「推送微信模板消息」的功能,只需要再写一个Observer类,完全不用动已有的代码。

25.6 整合:订单服务主流程

好了,四个模式都实现了。现在把它们串起来,看看一个完整的订单创建流程是什么样的。

@Service
public class OrderService {
    @Autowired
    private OrderHandlerFactory handlerFactory;
    @Autowired
    private PaymentContext paymentContext;
    @Autowired
    private OrderEventPublisher eventPublisher;
    @Autowired
    private OrderConfigSingleton config;  // 单例注入

    public void createOrder(OrderRequest request) {
        // 1. 创建订单对象
        Order order = new Order();
        order.setOrderId(UUID.randomUUID().toString());
        order.setType(request.getOrderType());
        order.setTotalAmount(request.getAmount());

        // 2. 工厂模式:获取对应的订单处理器
        OrderHandler handler = handlerFactory.getHandler(order.getType());
        handler.handle(order);

        // 3. 策略模式:执行支付
        boolean paid = paymentContext.executePayment(order, request.getPaymentChannel());
        if (!paid) {
            throw new RuntimeException("支付失败");
        }

        // 4. 观察者模式:发布支付成功事件
        eventPublisher.publishOrderEvent(order, "PAY_SUCCESS");

        System.out.println("订单创建完成: " + order.getOrderId());
    }
}

看到没?整个流程清晰得像流水线。每个模式只负责自己那一块,互不干扰。这就是设计模式组合使用的魅力。

25.7 避坑与经验总结

最后聊几个我在实战中踩过的坑,希望对你有帮助。

模式 常见坑点 我的建议
单例 线程安全问题、序列化破坏 用枚举实现,一劳永逸
工厂 类型过多导致工厂膨胀 结合Spring的依赖注入自动注册
策略 策略类数量爆炸 用Map管理,按需加载
观察者 事件处理异常导致主流程中断 异步处理,或者加try-catch
个人经验: 我曾经在一个项目中,观察者模式的事件处理抛出了异常,结果导致主流程回滚。那次事故让我意识到:观察者应该异步执行,或者至少保证异常不影响主流程。后来我改用@Async注解,或者用消息队列来做事件驱动,效果好了很多。

好了,这一章的内容就到这里。四个模式组合使用,其实并不复杂。关键是理解每个模式的职责边界,不要越界。工厂只负责创建,策略只负责算法,观察者只负责通知,单例只负责配置。各司其职,系统自然就清晰了。


公众号:蓝海资料掘金营,微信deep3321