100、设计模式综合实战:电商系统核心模块设计

终于到了最后一章。说实话,我写这个系列的时候,一直在想怎么收尾才够分量。光讲理论没意思,光贴代码也没劲。所以我决定,把前面99个模式揉到一起,做一个真正的电商系统核心模块。

你想想看,电商系统有多复杂?订单、商品、库存、支付、促销、物流……每个模块都牵扯一堆业务逻辑。如果不用设计模式,代码很快就会变成一团乱麻。我在上一家公司就见过一个订单模块,光一个类就三千多行,后来没人敢改。

这一章,我会带你从零搭建一个高扩展性的电商核心架构。我们会用到创建型、结构型、行为型三大类模式,把它们有机地组合起来。嗯,说白了,就是让你看看真正的实战长什么样。

整体架构概览

先看一张图,这是我个人习惯的做法——动手之前,先把结构画清楚。

电商系统核心模块架构图 表现层(Controller / Facade) 应用层(Service / 策略编排) 领域层(核心业务模型 + 设计模式) 订单(状态模式) 商品(原型模式) 促销(策略模式) 支付(工厂模式) 基础设施层(DAO / 缓存 / 消息队列) 分层架构

这张图里,我把系统分成了四层。每一层都有自己负责的设计模式。你仔细看领域层,那里才是设计模式真正发光的地方。

创建型模式:对象怎么来?

电商系统里,对象的创建是个大问题。商品有成千上万种,订单状态千变万化,支付渠道五花八门。如果到处 new 对象,后期维护就是噩梦。

工厂模式:支付渠道的创建

我记得有一次,公司要接入一个新的支付渠道——某某钱包。如果代码里到处都是 if-else 判断支付类型,那接入一个新渠道就得改好几个文件。我用工厂模式解决了这个问题。

// Java 示例:支付工厂
public interface Payment {
    boolean pay(Order order);
}

public class Alipay implements Payment {
    public boolean pay(Order order) {
        System.out.println("支付宝支付:" + order.getAmount());
        return true;
    }
}

public class WechatPay implements Payment {
    public boolean pay(Order order) {
        System.out.println("微信支付:" + order.getAmount());
        return true;
    }
}

public class PaymentFactory {
    private static Map<String, Payment> cache = new HashMap<>();
    
    static {
        cache.put("alipay", new Alipay());
        cache.put("wechat", new WechatPay());
    }
    
    public static Payment getPayment(String type) {
        Payment p = cache.get(type);
        if (p == null) {
            throw new IllegalArgumentException("不支持的支付类型:" + type);
        }
        return p;
    }
}
我的习惯:工厂里加一个缓存 Map,避免每次调用都创建新对象。支付渠道通常是单例的,没必要重复创建。

原型模式:商品快照

电商里有个常见需求——生成商品快照。用户下单时,商品的价格、描述、图片都可能变。如果直接引用商品对象,订单里的数据就跟着变了。这肯定不行。

我建议用原型模式,克隆一份当前的商品数据。

// Java 示例:商品快照
public class ProductSnapshot implements Cloneable {
    private String name;
    private double price;
    private String description;
    
    public ProductSnapshot(Product product) {
        this.name = product.getName();
        this.price = product.getPrice();
        this.description = product.getDescription();
    }
    
    @Override
    public ProductSnapshot clone() {
        try {
            return (ProductSnapshot) super.clone();
        } catch (CloneNotSupportedException e) {
            // 不会发生,因为实现了 Cloneable
            return null;
        }
    }
}
注意:浅克隆就够了,因为快照里的字段都是基本类型或不可变 String。如果字段里有 List 或自定义对象,记得做深克隆。

结构型模式:对象怎么组合?

创建完对象,接下来就是怎么把它们组合起来。电商系统里,最典型的就是订单和商品的关系,还有各种促销活动的叠加。

装饰器模式:促销叠加

双十一的时候,促销活动能叠好几层:满减、折扣、优惠券、会员价……如果每个促销都写一个 if,代码会膨胀到没法看。我用装饰器模式,把促销一层层包起来。

// Java 示例:促销装饰器
public interface PriceCalculator {
    double calculate(double originalPrice);
}

public class BasePrice implements PriceCalculator {
    public double calculate(double originalPrice) {
        return originalPrice;
    }
}

public class DiscountDecorator implements PriceCalculator {
    private PriceCalculator wrapper;
    private double discount;
    
    public DiscountDecorator(PriceCalculator wrapper, double discount) {
        this.wrapper = wrapper;
        this.discount = discount;
    }
    
    public double calculate(double originalPrice) {
        return wrapper.calculate(originalPrice) * discount;
    }
}

// 使用:满100打8折,再减20
PriceCalculator calc = new BasePrice();
calc = new DiscountDecorator(calc, 0.8);
calc = new CouponDecorator(calc, 20);
double finalPrice = calc.calculate(100); // 结果:60

核心思想:每个装饰器只做一件事。满减只管满减,折扣只管折扣。它们可以任意组合,互不干扰。

适配器模式:第三方物流

电商系统经常对接多家物流公司。每家物流的接口都不一样,有的用 HTTP,有的用 WebService,有的甚至用 FTP 传文件。适配器模式就是干这个的。

// Java 示例:物流适配器
public interface LogisticsAdapter {
    String createShipment(Order order);
    String trackShipment(String trackingNo);
}

public class SFAdapter implements LogisticsAdapter {
    public String createShipment(Order order) {
        // 调用顺丰的 HTTP API
        return "SF" + System.currentTimeMillis();
    }
    
    public String trackShipment(String trackingNo) {
        // 查询顺丰物流轨迹
        return "已揽收";
    }
}

public class YTOAdapter implements LogisticsAdapter {
    public String createShipment(Order order) {
        // 调用圆通的 WebService API
        return "YTO" + System.currentTimeMillis();
    }
    
    public String trackShipment(String trackingNo) {
        // 查询圆通物流轨迹
        return "运输中";
    }
}

行为型模式:对象怎么交互?

对象创建好了,组合好了,接下来就是它们之间的交互。订单状态流转、促销策略选择、支付结果通知……这些都是行为型模式的用武之地。

状态模式:订单生命周期

订单的状态变化是最典型的例子。待支付、已支付、已发货、已签收、已取消……每个状态能做什么、不能做什么,都有严格限制。我曾经见过用一堆 boolean 变量控制状态的代码,改一个状态要检查七八个条件,bug 频出。

// Java 示例:订单状态
public interface OrderState {
    void pay(OrderContext context);
    void ship(OrderContext context);
    void sign(OrderContext context);
    void cancel(OrderContext context);
}

public class UnpaidState implements OrderState {
    public void pay(OrderContext context) {
        System.out.println("支付成功,进入已支付状态");
        context.setState(new PaidState());
    }
    
    public void ship(OrderContext context) {
        throw new RuntimeException("未支付不能发货");
    }
    
    public void sign(OrderContext context) {
        throw new RuntimeException("未支付不能签收");
    }
    
    public void cancel(OrderContext context) {
        System.out.println("取消订单");
        context.setState(new CancelledState());
    }
}

public class PaidState implements OrderState {
    public void pay(OrderContext context) {
        throw new RuntimeException("已支付,不能重复支付");
    }
    
    public void ship(OrderContext context) {
        System.out.println("发货成功,进入已发货状态");
        context.setState(new ShippedState());
    }
    
    // ... 其他方法
}
避坑指南:我曾经在状态模式里忘了处理「已取消」状态的后续操作。结果用户取消订单后,还能发起退款。后来我加了一个 CancelledState,把所有操作都抛异常,问题就解决了。

观察者模式:库存同步

下单成功后,需要同步做几件事:扣减库存、发送短信、记录日志、更新缓存。如果把这些逻辑都写在订单服务里,那订单服务就太「胖」了。观察者模式可以把这些逻辑解耦。

// Java 示例:订单事件观察者
public interface OrderObserver {
    void onOrderCreated(Order order);
}

public class InventoryObserver implements OrderObserver {
    public void onOrderCreated(Order order) {
        System.out.println("扣减库存:" + order.getProductId());
    }
}

public class SmsObserver implements OrderObserver {
    public void onOrderCreated(Order order) {
        System.out.println("发送短信给:" + order.getUserId());
    }
}

public class OrderEventPublisher {
    private List<OrderObserver> observers = new ArrayList<>();
    
    public void addObserver(OrderObserver observer) {
        observers.add(observer);
    }
    
    public void notifyOrderCreated(Order order) {
        for (OrderObserver observer : observers) {
            observer.onOrderCreated(order);
        }
    }
}

模式整合:一个完整的下单流程

好了,现在我们把所有模式串起来。一个用户下单,背后发生了什么?

  1. 工厂模式:根据用户选择的支付方式,创建对应的支付对象。
  2. 原型模式:克隆商品快照,保存到订单中。
  3. 装饰器模式:计算最终价格,叠加所有促销活动。
  4. 状态模式:订单进入「待支付」状态。
  5. 观察者模式:通知库存、短信、日志等观察者。
  6. 适配器模式:如果需要发货,调用物流接口。

你看,每个模式各司其职,互不干扰。这就是高扩展性架构的核心——单一职责 + 开闭原则

总结

这一章的内容比较多,但核心就一句话:设计模式不是孤立使用的,它们需要组合起来解决实际问题

我个人习惯,在设计一个系统之前,先画一张架构图,把每个模块用到的模式标出来。然后从创建型开始,一步步搭建。遇到变化点,就用结构型模式去封装。最后用行为型模式,把对象之间的交互理顺。

嗯,这个系列到这里就结束了。希望这100个实战案例,能让你在设计模式这条路上少走一些弯路。代码写多了,你会发现,设计模式其实不是「学」出来的,是「用」出来的。

最后送你一句话:设计模式是工具,不是教条。用对了,事半功倍;用错了,画蛇添足。多写、多练、多思考,你也能成为架构高手。


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