Skip to content

设计模式工程落地

1. 这是什么

设计模式是对常见设计问题的经验总结。
工程落地的重点不是背名称,而是识别问题并选用合适的抽象方式。

一句话理解:

  • 设计模式不是面试术语
  • 它更像一组“面对常见变化点时的组织经验”

2. 为什么重要

设计模式能帮助系统更容易扩展和维护,但前提是用对场景。
真正高级的能力,是知道:

  • 什么时候该抽象
  • 什么时候不该抽象

用对了,代码会:

  • 更可扩展
  • 更易测试
  • 更容易替换实现

用错了,则会变成:

  • 过度设计
  • 阅读成本高
  • 维护负担重

3. 先建立直觉:先看变化点,再选模式

这是学习模式最关键的一步。

真正适合上模式的地方,通常都有某种稳定的变化点,例如:

  • 算法会变
  • 规则会变
  • 流程中的某些节点会变
  • 同一套主流程有多个变体

如果没有稳定变化点,却硬上抽象,最后往往只是:

  • 多了一层类
  • 没有真正收益

4. 核心内容

4.1 工厂模式

工厂模式适合:

  • 创建逻辑有分支
  • 对象创建过程不想散落在业务代码里

它的价值在于:

  • 把“创建什么、怎么创建”集中管理

4.2 策略模式

策略模式适合:

  • 同一件事有多种实现方式
  • 可以按条件切换算法或规则

典型场景:

  • 支付方式
  • 计费方式
  • 风控策略

4.3 模板方法

模板方法适合:

  • 主流程固定
  • 某些步骤允许子类覆写

它强调的是:

  • 流程框架稳定
  • 局部步骤可扩展

4.4 观察者模式

观察者模式适合:

  • 一个事件发生后,需要通知多个订阅者

典型场景:

  • 订单完成后通知积分、消息、日志、审计

4.5 责任链模式

责任链模式适合:

  • 一组规则按顺序依次处理
  • 每个节点只关心自己那一段判断或处理

典型场景:

  • 参数校验链
  • 风控检查链
  • 过滤处理链

5. 学习重点

这一章最重要的是掌握:

  • 先看问题,再选模式
  • 模式的收益和成本要一起评估
  • 策略模式适合“同类算法切换”
  • 责任链适合“多步规则串联”
  • 模板方法适合“主流程稳定、局部步骤可扩展”

6. 常见问题

6.1 为了模式而模式

这是最典型的误区。
如果模式没有真正解决变化点,最后只会增加复杂度。

6.2 抽象层次过多导致阅读成本上升

架构不是层数越多越高级。

6.3 没有稳定变化点却提前设计

这很容易把项目做成“过度工程化样板”。

7. 动手验证

这一节我用一个纯 Java demo,把策略模式和责任链模式放到后端业务场景里一起跑。

7.1 准备一个可运行示例

新建文件 DesignPatternDemo.java,内容如下:

java
import java.util.ArrayList;
import java.util.List;

public class DesignPatternDemo {
    interface PriceStrategy {
        int calc(int amount);
    }

    static class NormalPriceStrategy implements PriceStrategy {
        @Override
        public int calc(int amount) {
            return amount;
        }
    }

    static class VipPriceStrategy implements PriceStrategy {
        @Override
        public int calc(int amount) {
            return amount - 20;
        }
    }

    static class OrderPriceService {
        private final PriceStrategy strategy;

        OrderPriceService(PriceStrategy strategy) {
            this.strategy = strategy;
        }

        int finalPrice(int amount) {
            return strategy.calc(amount);
        }
    }

    interface Validator {
        void validate(OrderCommand command);
    }

    static class OrderCommand {
        final long userId;
        final int amount;
        final String productCode;

        OrderCommand(long userId, int amount, String productCode) {
            this.userId = userId;
            this.amount = amount;
            this.productCode = productCode;
        }
    }

    static class UserValidator implements Validator {
        @Override
        public void validate(OrderCommand command) {
            if (command.userId <= 0) {
                throw new IllegalArgumentException("invalid user");
            }
            System.out.println("validator-user-pass");
        }
    }

    static class AmountValidator implements Validator {
        @Override
        public void validate(OrderCommand command) {
            if (command.amount <= 0) {
                throw new IllegalArgumentException("invalid amount");
            }
            System.out.println("validator-amount-pass");
        }
    }

    static class ProductValidator implements Validator {
        @Override
        public void validate(OrderCommand command) {
            if (command.productCode == null || command.productCode.isBlank()) {
                throw new IllegalArgumentException("invalid product");
            }
            System.out.println("validator-product-pass");
        }
    }

    static class ValidationChain {
        private final List<Validator> validators = new ArrayList<>();

        void add(Validator validator) {
            validators.add(validator);
        }

        void validate(OrderCommand command) {
            for (Validator validator : validators) {
                validator.validate(command);
            }
        }
    }

    public static void main(String[] args) {
        OrderPriceService normal = new OrderPriceService(new NormalPriceStrategy());
        OrderPriceService vip = new OrderPriceService(new VipPriceStrategy());
        System.out.println("normalPrice=" + normal.finalPrice(100));
        System.out.println("vipPrice=" + vip.finalPrice(100));

        ValidationChain chain = new ValidationChain();
        chain.add(new UserValidator());
        chain.add(new AmountValidator());
        chain.add(new ProductValidator());

        chain.validate(new OrderCommand(1L, 100, "BOOK"));
        System.out.println("validationChainDone=true");
    }
}

7.2 编译并运行

bash
javac DesignPatternDemo.java
java DesignPatternDemo

7.3 你应该观察到什么

输出应包含这些关键信息:

text
normalPrice=100
vipPrice=80
validator-user-pass
validator-amount-pass
validator-product-pass
validationChainDone=true

7.4 每一行在验证什么

  • normalPrice=100vipPrice=80:说明策略模式适合在不改主流程的前提下切换算法
  • 三条 validator-...-pass:说明责任链模式适合把多步规则拆成独立节点
  • validationChainDone=true:说明整条校验流程可以由链式结构统一组织

8. 练习建议

下面这些练习做完,这一章会更扎实:

  • 在真实业务里找一个适合策略模式的场景
  • 用责任链处理一组校验规则
  • 总结常见模式在后端项目中的落地位置
  • 复盘一次“模式用过头”的代码案例

9. 自测问题

  • 为什么设计模式不能脱离场景使用?
  • 策略模式和模板方法分别适合哪类问题?
  • 什么情况下设计模式会变成过度设计?
  • 责任链模式为什么适合规则校验场景?

10. 自测核对要点

如果你的回答能覆盖下面这些点,说明这一章基本掌握到位了:

  • 模式应服务于变化点,而不是服务于炫技
  • 策略模式适合算法切换
  • 模板方法适合固定主流程下的局部扩展
  • 责任链适合多步规则串联
  • 过度抽象会直接增加系统复杂度