100、设计模式综合实战:电商系统核心模块设计
终于到了最后一章。说实话,我写这个系列的时候,一直在想怎么收尾才够分量。光讲理论没意思,光贴代码也没劲。所以我决定,把前面99个模式揉到一起,做一个真正的电商系统核心模块。
你想想看,电商系统有多复杂?订单、商品、库存、支付、促销、物流……每个模块都牵扯一堆业务逻辑。如果不用设计模式,代码很快就会变成一团乱麻。我在上一家公司就见过一个订单模块,光一个类就三千多行,后来没人敢改。
这一章,我会带你从零搭建一个高扩展性的电商核心架构。我们会用到创建型、结构型、行为型三大类模式,把它们有机地组合起来。嗯,说白了,就是让你看看真正的实战长什么样。
整体架构概览
先看一张图,这是我个人习惯的做法——动手之前,先把结构画清楚。
这张图里,我把系统分成了四层。每一层都有自己负责的设计模式。你仔细看领域层,那里才是设计模式真正发光的地方。
创建型模式:对象怎么来?
电商系统里,对象的创建是个大问题。商品有成千上万种,订单状态千变万化,支付渠道五花八门。如果到处 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;
}
}
原型模式:商品快照
电商里有个常见需求——生成商品快照。用户下单时,商品的价格、描述、图片都可能变。如果直接引用商品对象,订单里的数据就跟着变了。这肯定不行。
我建议用原型模式,克隆一份当前的商品数据。
// 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;
}
}
}
结构型模式:对象怎么组合?
创建完对象,接下来就是怎么把它们组合起来。电商系统里,最典型的就是订单和商品的关系,还有各种促销活动的叠加。
装饰器模式:促销叠加
双十一的时候,促销活动能叠好几层:满减、折扣、优惠券、会员价……如果每个促销都写一个 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());
}
// ... 其他方法
}
观察者模式:库存同步
下单成功后,需要同步做几件事:扣减库存、发送短信、记录日志、更新缓存。如果把这些逻辑都写在订单服务里,那订单服务就太「胖」了。观察者模式可以把这些逻辑解耦。
// 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);
}
}
}
模式整合:一个完整的下单流程
好了,现在我们把所有模式串起来。一个用户下单,背后发生了什么?
- 工厂模式:根据用户选择的支付方式,创建对应的支付对象。
- 原型模式:克隆商品快照,保存到订单中。
- 装饰器模式:计算最终价格,叠加所有促销活动。
- 状态模式:订单进入「待支付」状态。
- 观察者模式:通知库存、短信、日志等观察者。
- 适配器模式:如果需要发货,调用物流接口。
你看,每个模式各司其职,互不干扰。这就是高扩展性架构的核心——单一职责 + 开闭原则。
总结
这一章的内容比较多,但核心就一句话:设计模式不是孤立使用的,它们需要组合起来解决实际问题。
我个人习惯,在设计一个系统之前,先画一张架构图,把每个模块用到的模式标出来。然后从创建型开始,一步步搭建。遇到变化点,就用结构型模式去封装。最后用行为型模式,把对象之间的交互理顺。
嗯,这个系列到这里就结束了。希望这100个实战案例,能让你在设计模式这条路上少走一些弯路。代码写多了,你会发现,设计模式其实不是「学」出来的,是「用」出来的。
最后送你一句话:设计模式是工具,不是教条。用对了,事半功倍;用错了,画蛇添足。多写、多练、多思考,你也能成为架构高手。
公众号:蓝海资料掘金营,微信 deep3321