Java 23种设计模式实战:从电商订单系统出发,告别Hello World

写在前面:网上设计模式教程满天飞,但大多数都是 Animal → Dog → Cat 的玩具示例。本文用一套完整的电商/订单/支付/用户系统贯穿全部 23 种 GoF 模式,每个模式都有可运行的 main() 方法,控制台输出真实业务结果。全部代码已编译通过,文末附完整项目地址。


目录


一、为什么要学设计模式?

先看一段没学设计模式时可能会写的代码:

// 典型的"屎山"代码
public String payOrder(String orderId, String payMethod, String discountType) {
    // 支付逻辑:一堆 if-else
    if ("wechat".equals(payMethod)) {
        // 微信支付逻辑...
    } else if ("alipay".equals(payMethod)) {
        // 支付宝逻辑...
    } else if ("bank".equals(payMethod)) {
        // 银行卡逻辑...
    }

    // 折扣逻辑:又一堆 if-else
    if ("fullReduction".equals(discountType)) {
        // 满减...
    } else if ("coupon".equals(discountType)) {
        // 优惠券...
    } else if ("member".equals(discountType)) {
        // 会员折扣...
    }

    // 还混着权限校验、日志记录、缓存逻辑...
    return "success";
}

问题在哪?

  • 每加一种支付方式,就要改这个方法 → 违反开闭原则
  • 支付、折扣、权限、日志全混在一个方法里 → 违反单一职责
  • 想测试某种折扣逻辑,必须连支付逻辑一起跑 → 难以测试
  • 代码越来越长,没人敢动 → 技术债雪球

设计模式解决的就是这些问题。 不是为了装X,而是为了让代码:能扩展、好维护、易测试。


二、23种模式总览

分类 模式 业务场景 一句话理解
创建型 单例 Singleton 数据库连接池 全局只有一个实例
工厂方法 Factory Method 日志记录器 父类定接口,子类定实现
抽象工厂 Abstract Factory 云服务商资源 创建一组相关对象
建造者 Builder 订单构建 分步构建复杂对象
原型 Prototype 促销模板 clone() 快速复制
行为型 策略 Strategy 折扣/支付 算法可互换,消除if-else
责任链 Chain 审批流程 链式传递,各管一段
观察者 Observer 事件通知 发布-订阅,松耦合
状态 State 订单状态机 状态驱动行为
命令 Command 订单操作 请求封装为对象,可撤销
模板方法 Template 订单流程 骨架固定,步骤可重写
解释器 Interpreter SQL条件解析 字符串→AST→求值
访问者 Visitor 多格式导出 双分派,操作与数据分离
备忘录 Memento 状态快照 保存/恢复对象状态
迭代器 Iterator 分页遍历 统一遍历接口
中介者 Mediator 模块协调 多对多→多对一
结构型 代理 Proxy 权限/缓存/日志 控制访问,AOP底层
装饰器 Decorator 价格叠加 包装增强,不改原类
适配器 Adapter 支付SDK适配 转换接口
外观 Facade 支付入口 简化子系统调用
桥接 Bridge 消息发送 两个维度独立变化
组合 Composite 组织架构树 统一处理树形结构
享元 Flyweight 商品池 共享对象省内存

三、创建型模式(5种)—— 对象怎么创建

3.1 单例模式:数据库连接池

场景:数据库连接池必须全局唯一,不能每个模块各建一个。

5种实现方式对比

实现 线程安全 延迟加载 性能 防反射 推荐度
饿汉式 ⭐⭐
懒汉(同步方法)
双重检查锁(DCL) ⭐⭐⭐⭐
静态内部类 ⭐⭐⭐⭐⭐
枚举 ⭐⭐⭐⭐

DCL 实现(最常用)

class DatabaseConnectionPool {
    // volatile 防止指令重排
    private static volatile DatabaseConnectionPool instance;

    private String url;
    private int maxPoolSize;
    private int activeConnections = 0;

    private DatabaseConnectionPool() {}

    public static DatabaseConnectionPool getInstance() {
        if (instance == null) {                    // 第一次检查:避免不必要的加锁
            synchronized (DatabaseConnectionPool.class) {
                if (instance == null) {            // 第二次检查:防止重复创建
                    instance = new DatabaseConnectionPool();
                }
            }
        }
        return instance;
    }

    public Connection getConnection() {
        if (activeConnections >= maxPoolSize) {
            throw new RuntimeException("连接池已满");
        }
        activeConnections++;
        return new Connection(url, activeConnections);
    }
}

为什么要 volatile? instance = new DatabaseConnectionPool() 不是原子操作,分3步:1)分配内存 2)初始化对象 3)引用指向内存。如果不加 volatile,JVM 可能重排为 1→3→2,其他线程在步骤3之后、步骤2之前拿到一个未初始化的实例。

静态内部类实现(最优雅,推荐)

class ConnectionPoolHolder {
    private ConnectionPoolHolder() {}

    // 利用类加载机制保证线程安全,且延迟加载
    private static class Holder {
        private static final ConnectionPoolHolder INSTANCE = new ConnectionPoolHolder();
    }

    public static ConnectionPoolHolder getInstance() {
        return Holder.INSTANCE; // 只有第一次调用时才触发 Holder 类加载
    }
}

3.2 工厂方法:日志记录器

场景:开发环境用控制台日志,生产环境用文件日志,测试环境用内存日志。

// 产品接口
interface Logger {
    void log(String message);
    void error(String message);
}

// 具体产品
class ConsoleLogger implements Logger {
    public void log(String msg)   { System.out.println("[CONSOLE] " + msg); }
    public void error(String msg) { System.err.println("[CONSOLE-ERROR] " + msg); }
}

class FileLogger implements Logger {
    public void log(String msg)   { /* 写入文件 */ }
    public void error(String msg) { /* 写入文件 */ }
}

// 工厂接口
interface LoggerFactory {
    Logger createLogger();
}

// 具体工厂
class ConsoleLoggerFactory implements LoggerFactory {
    public Logger createLogger() { return new ConsoleLogger(); }
}

class FileLoggerFactory implements LoggerFactory {
    public Logger createLogger() { return new FileLogger(); }
}

核心思想:调用方不关心 new ConsoleLogger() 还是 new FileLogger(),只跟工厂接口打交道。

与简单工厂的区别:简单工厂是一个工厂类里用 switch-case 决定创建哪个;工厂方法是为每种产品各建一个工厂类,符合开闭原则。


3.3 抽象工厂:云服务商资源族

场景:系统要支持腾讯云和阿里云,每家云的云服务器、对象存储、数据库都不一样,但都是一组配套使用的。

// 抽象产品族
interface CloudServer     { void start(); void stop(); }
interface ObjectStorage   { void upload(String key, byte[] data); }
interface CloudDatabase   { void connect(); void execute(String sql); }

// 抽象工厂
interface CloudFactory {
    CloudServer createServer();
    ObjectStorage createStorage();
    CloudDatabase createDatabase();
}

// 腾讯云工厂
class TencentCloudFactory implements CloudFactory {
    public CloudServer createServer()   { return new CVM(); }
    public ObjectStorage createStorage() { return new COS(); }
    public CloudDatabase createDatabase() { return new TDSQL(); }
}

// 阿里云工厂
class AliyunFactory implements CloudFactory {
    public CloudServer createServer()   { return new ECS(); }
    public ObjectStorage createStorage() { return new OSS(); }
    public CloudDatabase createDatabase() { return new RDS(); }
}

工厂方法 vs 抽象工厂:工厂方法创建一个产品;抽象工厂创建一族产品。如果你需要一组配套使用的对象(选了腾讯云的服务器就得配腾讯云的存储),用抽象工厂。


3.4 建造者模式:订单构建

场景:一个订单有十几个字段,有些必填有些选填,构造函数参数爆炸。

// 链式调用构建
Order order = new Order.Builder("ORD-001", "张三")
    .product("MacBook Pro 14")
    .quantity(1)
    .unitPrice(14999.00)
    .shippingAddress("西安市雁塔区...")
    .paymentMethod("alipay")
    .couponCode("SUMMER100")
    .remark("工作日送达")
    .build();

实现要点

class Order {
    private final String orderId;     // 必填
    private final String customer;    // 必填
    private String product;           // 选填
    private int quantity;             // 选填
    private double unitPrice;         // 选填
    // ... 其他选填字段

    private Order(Builder builder) {
        this.orderId = builder.orderId;
        this.customer = builder.customer;
        this.product = builder.product;
        // ...
    }

    public static class Builder {
        private final String orderId;
        private final String customer;

        public Builder(String orderId, String customer) {
            this.orderId = orderId;
            this.customer = customer;
        }

        public Builder product(String product)      { this.product = product; return this; }
        public Builder quantity(int quantity)        { this.quantity = quantity; return this; }
        public Builder unitPrice(double price)       { this.unitPrice = price; return this; }

        public Order build() {
            return new Order(this);
        }
    }
}

Lombok 的 @Builder 注解就是干这个的,底层自动生成上面的代码。


3.5 原型模式:促销模板复制

场景:运营创建了一个"双11满减促销"模板,之后要基于它快速创建几十个类似的促销活动,一个个 new 太慢。

class PromotionTemplate implements Cloneable {
    String name;
    String type;       // FULL_REDUCTION / COUPON / DISCOUNT
    double threshold;
    double discount;
    List<String> applicableProducts;

    @Override
    public PromotionTemplate clone() {
        try {
            PromotionTemplate cloned = (PromotionTemplate) super.clone();
            // 深拷贝:列表也要复制
            cloned.applicableProducts = new ArrayList<>(this.applicableProducts);
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

使用

PromotionTemplate template = new PromotionTemplate();
template.name = "双11满减";
template.type = "FULL_REDUCTION";
template.threshold = 1000;
template.discount = 100;
template.applicableProducts = Arrays.asList("电子产品", "家居");

// 基于模板快速创建
PromotionTemplate electronics = template.clone();
electronics.name = "电子产品专场满减";
electronics.applicableProducts.add("数码配件");

PromotionTemplate homeAppliances = template.clone();
homeAppliances.name = "家居专场满减";
homeAppliances.threshold = 500;

深拷贝 vs 浅拷贝super.clone() 是浅拷贝,引用类型字段会共享同一个对象。如果模板里有个 List,两个克隆体改的是同一个 List。所以必须手动深拷贝引用类型字段。


四、行为型模式(11种)—— 对象怎么协作

4.1 策略模式:折扣与支付

场景:订单系统有多种折扣方式(不打折/满减/优惠券/会员折)和多种支付方式(微信/支付宝/银行卡),运行时动态切换。

不用策略模式

// 每加一种折扣就要改这个方法
double calculatePrice(double original, String discountType) {
    if ("none".equals(discountType)) return original;
    if ("fullReduction".equals(discountType)) return original >= 1000 ? original - 100 : original;
    if ("coupon".equals(discountType)) return original - 50;
    if ("member".equals(discountType)) return original * 0.8;
    return original;
}

用策略模式

// 策略接口
interface DiscountStrategy {
    double calculate(double originalPrice);
}

// 四种策略实现
class NoDiscountStrategy implements DiscountStrategy {
    public double calculate(double price) { return price; }
}

class FullReductionStrategy implements DiscountStrategy {
    private double threshold, reduction;
    FullReductionStrategy(double threshold, double reduction) {
        this.threshold = threshold; this.reduction = reduction;
    }
    public double calculate(double price) {
        return price >= threshold ? price - reduction : price;
    }
}

class CouponStrategy implements DiscountStrategy {
    private double couponValue;
    CouponStrategy(double couponValue) { this.couponValue = couponValue; }
    public double calculate(double price) { return Math.max(0, price - couponValue); }
}

class MemberDiscountStrategy implements DiscountStrategy {
    private double rate;
    MemberDiscountStrategy(double rate) { this.rate = rate; }
    public double calculate(double price) { return price * rate; }
}

运行时切换

double originalPrice = 1000.00;

// 策略1:不打折
DiscountStrategy s1 = new NoDiscountStrategy();
System.out.println("不打折: ¥" + s1.calculate(originalPrice));       // 1000.0

// 策略2:满1000减100
DiscountStrategy s2 = new FullReductionStrategy(1000, 100);
System.out.println("满减: ¥" + s2.calculate(originalPrice));         // 900.0

// 策略3:会员8折
DiscountStrategy s3 = new MemberDiscountStrategy(0.8);
System.out.println("会员折: ¥" + s3.calculate(originalPrice));       // 800.0

新增折扣方式时:只需要新增一个实现类,不改任何已有代码。这就是开闭原则。


4.2 责任链模式:订单审批流

场景:退款审批,金额不同走不同审批人:<1000 客服批 → <10000 经理批 → <100000 总监批 → ≥100000 CEO批。

// 抽象审批者
abstract class Approver {
    protected Approver next;       // 下一个审批者
    protected String name;
    protected double maxAmount;    // 审批权限上限

    void setNext(Approver next) { this.next = next; }

    void approve(RefundRequest req) {
        if (req.amount <= maxAmount) {
            doApprove(req);                    // 权限内,直接批
        } else if (next != null) {
            System.out.println(name + ": 超出权限,转交上级");
            next.approve(req);                 // 传给下一个
        } else {
            System.out.println(name + ": 无人可审批此金额");
        }
    }

    abstract void doApprove(RefundRequest req);
}

// 具体审批者
class CustomerServiceApprover extends Approver {
    CustomerServiceApprover() { this.name = "客服"; this.maxAmount = 1000; }
    void doApprove(RefundRequest req) {
        System.out.println("客服: 批准退款 " + req.requestId + " ¥" + req.amount);
    }
}

class ManagerApprover extends Approver {
    ManagerApprover() { this.name = "部门经理"; this.maxAmount = 10000; }
    void doApprove(RefundRequest req) {
        System.out.println("经理: 批准退款 " + req.requestId + " ¥" + req.amount);
    }
}

class DirectorApprover extends Approver {
    DirectorApprover() { this.name = "总监"; this.maxAmount = 100000; }
    void doApprove(RefundRequest req) {
        System.out.println("总监: 批准退款 " + req.requestId + " ¥" + req.amount);
    }
}

class CeoApprover extends Approver {
    CeoApprover() { this.name = "CEO"; this.maxAmount = Double.MAX_VALUE; }
    void doApprove(RefundRequest req) {
        System.out.println("CEO: 批准退款 " + req.requestId + " ¥" + req.amount);
    }
}

构建审批链

// 组装责任链
Approver customerService = new CustomerServiceApprover();
Approver manager = new ManagerApprover();
Approver director = new DirectorApprover();
Approver ceo = new CeoApprover();

customerService.setNext(manager);
manager.setNext(director);
director.setNext(ceo);

// 测试不同金额
customerService.approve(new RefundRequest("R001", 500,   "商品质量问题"));  // 客服批
customerService.approve(new RefundRequest("R002", 5000,  "发错货"));        // 经理批
customerService.approve(new RefundRequest("R003", 50000, "大批量退货"));     // 总监批
customerService.approve(new RefundRequest("R004", 200000,"企业客户退款"));   // CEO批

运行输出

客服: 批准退款 R001 ¥500.0
---
客服: 超出权限,转交上级
部门经理: 批准退款 R002 ¥5000.0
---
客服: 超出权限,转交上级
部门经理: 超出权限,转交上级
总监: 批准退款 R003 ¥50000.0
---
客服: 超出权限,转交上级
部门经理: 超出权限,转交上级
总监: 超出权限,转交上级
CEO: 批准退款 R004 ¥200000.0

Spring 中的责任链HandlerInterceptorChain 就是责任链模式,每个拦截器决定放行还是拦截。


4.3 观察者模式:订单事件通知

场景:用户下单后,需要同时触发:发短信通知、发邮件确认、扣减库存、推送数据给BI系统。这些操作互相独立,且未来可能增加新的通知方式。

// 事件
class OrderEvent {
    String orderId;
    String userId;
    double amount;
    // ...
}

// 观察者接口
interface OrderEventListener {
    void onOrderCreated(OrderEvent event);
}

// 多个监听器
class SmsNotifier implements OrderEventListener {
    public void onOrderCreated(OrderEvent event) {
        System.out.println("[短信] 通知用户 " + event.userId + " 订单 " + event.orderId + " 已创建");
    }
}

class EmailNotifier implements OrderEventListener {
    public void onOrderCreated(OrderEvent event) {
        System.out.println("[邮件] 发送订单确认邮件: " + event.orderId);
    }
}

class InventoryDeductor implements OrderEventListener {
    public void onOrderCreated(OrderEvent event) {
        System.out.println("[库存] 扣减订单 " + event.orderId + " 对应商品库存");
    }
}

// 被观察者(事件发布者)
class OrderService {
    private List<OrderEventListener> listeners = new ArrayList<>();

    void addListener(OrderEventListener listener) { listeners.add(listener); }

    void createOrder(OrderEvent event) {
        System.out.println("订单创建: " + event.orderId);
        // 通知所有监听器
        for (OrderEventListener listener : listeners) {
            listener.onOrderCreated(event);
        }
    }
}

使用

OrderService orderService = new OrderService();
orderService.addListener(new SmsNotifier());
orderService.addListener(new EmailNotifier());
orderService.addListener(new InventoryDeductor());

orderService.createOrder(new OrderEvent("ORD-001", "U10086", 1299.00));

新增通知方式:只需新增一个 XxxNotifier implements OrderEventListener,注册进去就行,完全不改 OrderService


4.4 状态模式:订单状态机

场景:订单有多个状态(待支付→已支付→已发货→已完成/已取消),不同状态下能做的操作不同。

不用状态模式(满屏if-else)

void pay(Order order) {
    if (order.status.equals("PENDING")) {
        order.status = "PAID";
    } else if (order.status.equals("PAID")) {
        throw new RuntimeException("已支付,不能重复支付");
    } else if (order.status.equals("SHIPPED")) {
        throw new RuntimeException("已发货,不能支付");
    }
    // ... 每个操作都要判断所有状态
}

用状态模式

// 状态接口
interface OrderState {
    void pay(OrderContext order);
    void ship(OrderContext order);
    void deliver(OrderContext order);
    void cancel(OrderContext order);
    String getStateName();
}

// 待支付状态
class PendingState implements OrderState {
    public void pay(OrderContext order) {
        System.out.println("支付成功");
        order.setState(new PaidState());
    }
    public void ship(OrderContext order)  { System.out.println("待支付状态不可发货"); }
    public void deliver(OrderContext order) { System.out.println("待支付状态不可确认收货"); }
    public void cancel(OrderContext order) {
        System.out.println("订单已取消");
        order.setState(new CancelledState());
    }
    public String getStateName() { return "待支付"; }
}

// 已支付状态
class PaidState implements OrderState {
    public void pay(OrderContext order) { System.out.println("已支付,请勿重复支付"); }
    public void ship(OrderContext order) {
        System.out.println("已发货");
        order.setState(new ShippedState());
    }
    public void deliver(OrderContext order) { System.out.println("已支付但未发货,不可确认收货"); }
    public void cancel(OrderContext order) {
        System.out.println("已支付订单取消,发起退款");
        order.setState(new CancelledState());
    }
    public String getStateName() { return "已支付"; }
}
// ... ShippedState, DeliveredState, CancelledState 类似

// 上下文
class OrderContext {
    private OrderState state;

    OrderContext() { this.state = new PendingState(); }

    void setState(OrderState state) {
        this.state = state;
        System.out.println("状态变更为: " + state.getStateName());
    }

    void pay()     { state.pay(this); }
    void ship()    { state.ship(this); }
    void deliver() { state.deliver(this); }
    void cancel()  { state.cancel(this); }
}

核心区别:状态模式中,状态自己决定下一步跳到哪个状态。策略模式中,外部决定用哪个策略。两者结构几乎一样,但语义不同。


4.5 装饰器模式:订单价格叠加

场景:订单最终价格 = 商品原价 → 满减 → 优惠券 → 会员折扣 → 积分抵扣。每一层优惠都是"叠加"在前一层之上。

// 核心接口
interface PriceCalculator {
    double calculate();
}

// 基础实现:原价
class BasePrice implements PriceCalculator {
    private double price;
    BasePrice(double price) { this.price = price; }
    public double calculate() { return price; }
}

// 装饰器基类(持有被装饰对象的引用)
abstract class PriceDecorator implements PriceCalculator {
    protected PriceCalculator wrapped;
    PriceDecorator(PriceCalculator wrapped) { this.wrapped = wrapped; }
}

// 满减装饰器
class FullReductionDecorator extends PriceDecorator {
    private double threshold, reduction;
    FullReductionDecorator(PriceCalculator wrapped, double threshold, double reduction) {
        super(wrapped);
        this.threshold = threshold;
        this.reduction = reduction;
    }
    public double calculate() {
        double price = wrapped.calculate();    // 先算前一层的价格
        return price >= threshold ? price - reduction : price;
    }
}

// 优惠券装饰器
class CouponDecorator extends PriceDecorator {
    private double couponValue;
    CouponDecorator(PriceCalculator wrapped, double couponValue) {
        super(wrapped);
        this.couponValue = couponValue;
    }
    public double calculate() {
        return Math.max(0, wrapped.calculate() - couponValue);
    }
}

// 会员折扣装饰器
class MemberDiscountDecorator extends PriceDecorator {
    private double rate;
    MemberDiscountDecorator(PriceCalculator wrapped, double rate) {
        super(wrapped);
        this.rate = rate;
    }
    public double calculate() {
        return wrapped.calculate() * rate;
    }
}

自由组合叠加

double basePrice = 1000.00;

// 方案1:只有原价 → 1000
PriceCalculator p1 = new BasePrice(basePrice);

// 方案2:原价 + 满减(满1000减100) → 900
PriceCalculator p2 = new FullReductionDecorator(
    new BasePrice(basePrice), 1000, 100);

// 方案3:原价 + 满减 + 优惠券(-50) → 850
PriceCalculator p3 = new CouponDecorator(
    new FullReductionDecorator(
        new BasePrice(basePrice), 1000, 100), 50);

// 方案4:原价 + 满减 + 优惠券 + 会员9折 → 765
PriceCalculator p4 = new MemberDiscountDecorator(
    new CouponDecorator(
        new FullReductionDecorator(
            new BasePrice(basePrice), 1000, 100), 50), 0.9);

装饰器 vs 代理:结构几乎一样(都持有目标对象引用),但意图不同——装饰器是"增强功能",代理是"控制访问"。


4.6 代理模式:用户服务权限控制

场景:用户服务接口需要权限校验 + 访问日志 + 结果缓存,但不想把这些横切逻辑写进 UserServiceImpl

// 接口
interface UserService {
    String getUser(String userId);
    void deleteUser(String userId);
}

// 真实实现(只关心业务)
class UserServiceImpl implements UserService {
    public String getUser(String userId) {
        try { Thread.sleep(50); } catch (Exception e) {} // 模拟DB查询
        System.out.println("[DB] 查询用户 " + userId);
        return "用户信息: {id=" + userId + ", name=张三}";
    }
    public void deleteUser(String userId) {
        System.out.println("[DB] 删除用户 " + userId);
    }
}

// 代理类:权限 + 日志 + 缓存
class UserServiceProxy implements UserService {
    private UserService target;
    private String role;
    private Map<String, String> cache = new HashMap<>();

    UserServiceProxy(UserService target, String role) {
        this.target = target;
        this.role = role;
    }

    public String getUser(String userId) {
        long start = System.currentTimeMillis();

        // 1. 缓存检查
        if (cache.containsKey(userId)) {
            System.out.println("[代理] 命中缓存");
            return cache.get(userId);
        }

        // 2. 调用真实方法
        String result = target.getUser(userId);

        // 3. 写入缓存
        cache.put(userId, result);

        // 4. 记录日志
        System.out.println("[代理] 耗时=" + (System.currentTimeMillis() - start) + "ms");
        return result;
    }

    public void deleteUser(String userId) {
        // 权限校验
        if (!"ADMIN".equals(role)) {
            System.out.println("[代理] 权限不足: " + role);
            return;
        }
        System.out.println("[代理] 访问日志: deleteUser(" + userId + ")");
        target.deleteUser(userId);
        cache.remove(userId); // 清缓存
    }
}

Spring AOP 底层就是这个@Transactional@Cacheable@Async 等注解,都是通过动态代理在方法前后织入横切逻辑。


4.7 解释器模式:SQL条件解析器

场景:运营后台需要根据用户输入的条件表达式动态过滤订单,如 status = 'PAID' AND amount > 1000 OR region = 'EAST'

语法规则(BNF)

expr   → term ('OR' term)*
term   → factor ('AND' factor)*
factor → 'key op value' | '(' expr ')'
op     → '=' | '!=' | '>' | '<' | '>=' | '<='

抽象表达式

interface Expression {
    boolean interpret(Map<String, Object> context);
}

// 终结符表达式:key op value
class ComparisonExpression implements Expression {
    private final String key;
    private final String operator;
    private final Object value;

    public boolean interpret(Map<String, Object> context) {
        Object actual = context.get(key);
        if (actual == null) return false;

        switch (operator) {
            case "=":  return actual.toString().equals(value.toString());
            case "!=": return !actual.toString().equals(value.toString());
            case ">":  return toDouble(actual) > toDouble(value);
            case "<":  return toDouble(actual) < toDouble(value);
            case ">=": return toDouble(actual) >= toDouble(value);
            case "<=": return toDouble(actual) <= toDouble(value);
            default:   return false;
        }
    }
    // ...
}

// 非终结符:AND
class AndExpression implements Expression {
    private final Expression left, right;
    public boolean interpret(Map<String, Object> ctx) {
        return left.interpret(ctx) && right.interpret(ctx);
    }
}

// 非终结符:OR
class OrExpression implements Expression {
    private final Expression left, right;
    public boolean interpret(Map<String, Object> ctx) {
        return left.interpret(ctx) || right.interpret(ctx);
    }
}

递归下降解析器

class ExpressionParser {
    private final List<String> tokens;
    private int pos = 0;

    // expr → term ('OR' term)*
    Expression parse() {
        Expression result = parseTerm();
        while (pos < tokens.size() && tokens.get(pos).equalsIgnoreCase("OR")) {
            pos++;
            result = new OrExpression(result, parseTerm());
        }
        return result;
    }

    // term → factor ('AND' factor)*
    private Expression parseTerm() {
        Expression result = parseFactor();
        while (pos < tokens.size() && tokens.get(pos).equalsIgnoreCase("AND")) {
            pos++;
            result = new AndExpression(result, parseFactor());
        }
        return result;
    }

    // factor → 'key op value' | '(' expr ')'
    private Expression parseFactor() {
        if (tokens.get(pos).equals("(")) {
            pos++;                              // 跳过 '('
            Expression result = parse();         // 递归解析括号内
            pos++;                              // 跳过 ')'
            return result;
        }
        String key = tokens.get(pos++);
        String op  = tokens.get(pos++);
        String val = tokens.get(pos++);
        return new ComparisonExpression(key, op, val);
    }
}

使用

List<Map<String, Object>> orders = List.of(
    Map.of("id", "ORD-001", "status", "PAID", "amount", 1500, "region", "EAST"),
    Map.of("id", "ORD-002", "status", "PENDING", "amount", 500, "region", "WEST"),
    Map.of("id", "ORD-003", "status", "PAID", "amount", 800, "region", "EAST")
);

String expr = "status = 'PAID' AND amount > 1000";
Expression ast = new ExpressionParser(tokenize(expr)).parse();

for (Map<String, Object> order : orders) {
    if (ast.interpret(order)) {
        System.out.println("匹配: " + order.get("id"));
    }
}
// 输出: 匹配: ORD-001

4.8 访问者模式:订单多格式导出

场景:订单有3种类型(普通/团购/预售),需要支持3种导出格式(PDF/Excel/JSON),每种格式对每种订单的导出逻辑都不同。3×3=9种组合。

// Element 接口
interface Order {
    String getOrderId();
    void accept(OrderVisitor visitor);  // 关键:accept 方法
}

// 三种订单
class NormalOrder implements Order {
    // ... 字段省略
    public void accept(OrderVisitor visitor) {
        visitor.visit(this);  // 第二次分派:根据visitor的实际类型调对应的visit
    }
}

class GroupBuyOrder implements Order {
    public void accept(OrderVisitor visitor) {
        visitor.visit(this);
    }
}

class PreSaleOrder implements Order {
    public void accept(OrderVisitor visitor) {
        visitor.visit(this);
    }
}

// Visitor 接口:每种订单一个方法
interface OrderVisitor {
    void visit(NormalOrder order);
    void visit(GroupBuyOrder order);
    void visit(PreSaleOrder order);
}

// 三种导出格式
class PdfExportVisitor implements OrderVisitor {
    public void visit(NormalOrder order)   { System.out.println("[PDF] 普通订单 " + order.getOrderId()); }
    public void visit(GroupBuyOrder order) { System.out.println("[PDF] 团购订单 " + order.getOrderId()); }
    public void visit(PreSaleOrder order)  { System.out.println("[PDF] 预售订单 " + order.getOrderId()); }
}

class ExcelExportVisitor implements OrderVisitor { /* ... */ }
class JsonExportVisitor implements OrderVisitor  { /* ... */ }

使用

List<Order> orders = List.of(
    new NormalOrder("ORD-001", "张三", 1299.00),
    new GroupBuyOrder("ORD-002", "李四", 899.00, 50),
    new PreSaleOrder("ORD-003", "赵六", 4999.00, "2026-09-01")
);

// 切换导出格式只需换 Visitor
OrderVisitor pdfVisitor = new PdfExportVisitor();
for (Order order : orders) {
    order.accept(pdfVisitor);  // 双分派自动定位到正确的方法
}

双分派机制

order.accept(visitor)
  ↓ 第一次分派:根据 order 的实际类型(NormalOrder)
  ↓ 调用 NormalOrder.accept()
    ↓ visitor.visit(this)
    ↓ 第二次分派:根据 visitor 的实际类型(PdfExportVisitor)
    ↓ 调用 PdfExportVisitor.visit(NormalOrder)
    ↓ 精确定位到唯一的方法

何时用访问者:数据结构类型固定(不常加新类型),但操作经常变化(常加新导出格式)。如果经常加新订单类型,每个 Visitor 接口都要改,反而麻烦。


4.9 命令/模板方法/备忘录/迭代器/中介者

这5种模式篇幅原因不展开完整代码,一句话总结:

模式 场景 核心思想 Spring映射
命令 Command 订单操作(创建/取消/退款)+ 撤销 把请求封装成对象,可排队、可撤销 RunnableJdbcTemplate
模板方法 Template 订单处理流程(标准/团购/预售) 父类定骨架,子类重写步骤 JdbcTemplateRestTemplate
备忘录 Memento 订单状态快照与回滚 保存对象状态,可恢复 WebFlux checkpoint
迭代器 Iterator 订单列表分页遍历 统一遍历接口,不暴露内部结构 IteratorStream
中介者 Mediator 电商模块协调(库存/支付/物流) 多对多通信→通过中介者 DispatcherServlet

五、结构型模式(7种)—— 对象怎么组合

模式 场景 核心思想 与谁容易混淆
代理 Proxy 用户服务权限/缓存/日志 控制访问 装饰器(都是持有引用,但意图不同)
装饰器 Decorator 订单价格叠加 包装增强功能 代理
适配器 Adapter 第三方支付SDK适配 转换接口 外观(都是简化调用)
外观 Facade 支付入口统一接口 简化子系统 适配器
桥接 Bridge 消息发送(渠道×格式) 两个维度独立变化 策略(都是委托,但桥接是结构型)
组合 Composite 组织架构树 统一处理树形结构
享元 Flyweight 商品信息共享池 共享细粒度对象

结构型模式的选择:想控制访问用代理,想增强功能用装饰器,想转换接口用适配器,想简化调用用外观,想拆分维度用桥接,想处理树用组合,想省内存用享元。


六、易混淆模式对比

6.1 策略 vs 状态

策略模式 状态模式
谁决定 外部(Context.setStrategy) 状态自己(State内部setState)
变化频率 一次请求内不变 每次操作后可能变
典型场景 选一种折扣算法 订单状态流转
结构 几乎一样 几乎一样

6.2 装饰器 vs 代理

装饰器模式 代理模式
意图 增强功能 控制访问
谁创建被包装对象 调用方传入 代理自己创建/管理
典型场景 价格叠加优惠 权限/缓存/远程代理
结构 几乎一样 几乎一样

6.3 适配器 vs 外观

适配器模式 外观模式
转换对象 一个接口 一组子系统
目的 兼容旧接口 简化调用
典型场景 第三方SDK适配 支付入口统一调用

6.4 工厂方法 vs 抽象工厂

工厂方法 抽象工厂
创建数量 一个产品 一族产品
扩展方式 加工厂子类 加工厂实现
典型场景 日志记录器 云服务商资源族

6.5 观察者 vs 中介者

观察者模式 中介者模式
通信方向 一对多广播 多对多通过中介
耦合方式 发布者不知道订阅者 各方只知道中介者
典型场景 事件通知 UI组件协调

6.6 命令 vs 备忘录

命令模式 备忘录模式
撤销方式 执行反向操作 恢复历史快照
存储内容 操作命令 对象完整状态
典型场景 订单操作撤销 订单状态回滚

七、设计原则对照

原则 含义 体现的模式
开闭原则 (OCP) 对扩展开放,对修改关闭 策略、观察者、访问者
单一职责 (SRP) 一个类只做一件事 代理、装饰器、外观
里氏替换 (LSP) 子类能替换父类 工厂方法、策略
依赖倒置 (DIP) 依赖抽象不依赖具体 工厂方法、桥接、策略
接口隔离 (ISP) 接口最小化 适配器、外观
迪米特法则 (LoD) 最少知道原则 外观、中介者

八、Spring框架中的设计模式

学设计模式最实际的意义是——你看Spring源码时能看懂

设计模式 Spring中的体现
单例 @Component@Service 默认单例
工厂方法 BeanFactoryFactoryBean
抽象工厂 ApplicationContext 体系
代理 AOP(@Transactional@Async@Cacheable
模板方法 JdbcTemplateRestTemplateTransactionTemplate
观察者 ApplicationEvent + ApplicationListener
责任链 HandlerInterceptorChain
策略 Resource 接口(ClassPathResource/FileSystemResource)
装饰器 TransactionAwareCacheDecoratorHttpServletRequestWrapper
适配器 HandlerAdapter(适配不同Controller类型)
外观 JdbcTemplate(封装JDBC复杂操作)
组合 CompositeViewMenuComponent
享元 Flyweight 模式的字符串常量池

九、学习路径建议

第一阶段:先吃透5个高频模式(1周)

策略模式 → 工厂方法 → 单例 → 观察者 → 代理

这5个在Spring中用得最多,学完之后看Spring源码会有"原来如此"的感觉。

第二阶段:创建型+结构型补全(1周)

工厂方法 → 抽象工厂 → 建造者 → 原型
适配器 → 桥接 → 组合 → 装饰器 → 外观 → 享元

第三阶段:行为型进阶(1周)

责任链 → 命令 → 迭代器 → 中介者 → 备忘录 → 状态 → 模板方法 → 解释器 → 访问者

学习方法

  1. 先跑代码:每个文件都有 main(),先运行看输出
  2. 看注释对比:文件头注释里有"不用模式会怎样"的对比
  3. 改一改:试着新增一种策略/审批人/导出格式,感受开闭原则
  4. 联系Spring:想想日常用的Spring注解对应哪个模式

完整代码

<!-- pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.devkit</groupId>
    <artifactId>java-design-patterns</artifactId>
    <version>1.0.0</version>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
</project>

编译运行

# 编译全部
mvn compile

# 运行单个模式
java -cp target/classes com.devkit.patterns.creational.Singleton_DatabaseConnectionPool
java -cp target/classes com.devkit.patterns.behavioral.Strategy_DiscountAndPayment
java -cp target/classes com.devkit.patterns.structural.Proxy_UserService

项目结构

java-design-patterns/
├── pom.xml
└── src/main/java/com/devkit/patterns/
    ├── creational/   5个文件
    ├── behavioral/  11个文件
    └── structural/   7个文件

总结

维度 说明
模式不是目的 解决问题才是。别为了用模式而用模式
先有问题再选模式 遇到if-else堆积 → 考虑策略;遇到审批流 → 考虑责任链
不要过度设计 3个if-else不需要策略模式,直接写更清晰
模式之间不是孤立的 Spring AOP用代理+责任链+模板方法,实际项目经常组合使用
模式是语言 和团队沟通时,说"这里用策略模式"比说"搞个接口搞几个实现类运行时切换"高效得多

如果觉得有帮助,点个赞收个藏,有问题欢迎评论区交流。

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐