设计模式与扩展性:工厂模式支持新产品、策略模式支持新算法、观察者支持新监听

做架构设计这么多年,我越来越觉得一个道理:好的设计不是把今天的功能写完,而是给明天的变化留好接口。说白了,扩展性才是衡量架构水平的关键指标。

今天咱们聊聊三种最常用的扩展性模式——工厂模式、策略模式、观察者模式。这三种模式分别解决了三类扩展问题:新产品接入、新算法替换、新监听注册。我敢说,你项目中90%的扩展需求,都能用这三板斧搞定。

1. 工厂模式:让新产品像插U盘一样简单

工厂模式的核心思想很简单:把对象的创建和使用分离。你想想看,如果每次新增一个产品类,都要去修改调用方的代码,那系统就太脆弱了。

我在一个电商项目中遇到过这样的场景:一开始只卖实体商品,后来要支持虚拟商品、数字商品、订阅服务。如果直接用 new 关键字创建对象,每加一种商品类型,所有下单的地方都得改——这谁受得了?

经典实现:简单工厂 + 反射

// Java 示例:商品工厂
public interface Product {
    void process();
}

public class PhysicalProduct implements Product {
    public void process() {
        System.out.println("处理实体商品:发货");
    }
}

public class VirtualProduct implements Product {
    public void process() {
        System.out.println("处理虚拟商品:发送兑换码");
    }
}

public class ProductFactory {
    private static Map<String, Class<? extends Product>> registry = new HashMap<>();
    
    public static void register(String type, Class<? extends Product> clazz) {
        registry.put(type, clazz);
    }
    
    public static Product create(String type) {
        Class<? extends Product> clazz = registry.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("未知商品类型: " + type);
        }
        return clazz.getDeclaredConstructor().newInstance();
    }
}

// 使用方式
ProductFactory.register("physical", PhysicalProduct.class);
ProductFactory.register("virtual", VirtualProduct.class);
Product product = ProductFactory.create("physical");
product.process();

扩展性要点:新增商品类型时,只需实现 Product 接口,并在工厂中注册。调用方代码完全不用改。

我的习惯:我会把注册逻辑放在 Spring 的 @PostConstruct 或者静态代码块中,这样系统启动时就自动完成注册。新来的同事只需要写一个类,加一个注解,完事。

2. 策略模式:算法想换就换,不用改业务逻辑

策略模式解决的是「同一件事,不同做法」的问题。比如支付方式、折扣计算、排序算法——这些场景下,算法本身经常变化,但调用算法的业务逻辑是稳定的。

我记得有一次做促销系统,运营团队每周都要调整满减策略。一开始我写了一大堆 if-else,后来发现每次改策略都要重新部署——太痛苦了。改用策略模式后,运营直接在配置中心改参数,系统热加载新策略,爽多了。

策略模式的标准结构

// C++ 示例:折扣策略
class DiscountStrategy {
public:
    virtual double calculate(double price) = 0;
    virtual ~DiscountStrategy() = default;
};

class NoDiscount : public DiscountStrategy {
public:
    double calculate(double price) override {
        return price;
    }
};

class PercentageDiscount : public DiscountStrategy {
private:
    double percent;
public:
    PercentageDiscount(double p) : percent(p) {}
    double calculate(double price) override {
        return price * (1 - percent);
    }
};

class FullReductionDiscount : public DiscountStrategy {
private:
    double threshold;
    double reduction;
public:
    FullReductionDiscount(double t, double r) : threshold(t), reduction(r) {}
    double calculate(double price) override {
        return price >= threshold ? price - reduction : price;
    }
};

// 上下文类
class Order {
private:
    DiscountStrategy* strategy;
public:
    void setStrategy(DiscountStrategy* s) { strategy = s; }
    double finalPrice(double price) {
        return strategy->calculate(price);
    }
};

扩展性要点:新增一种折扣算法,只需要写一个新类继承 DiscountStrategy。业务代码 Order 类完全不需要修改——这就是「开闭原则」的体现。

我曾经踩过的坑:策略模式虽然好,但别滥用。如果算法只有两三种,而且几乎不变,用 if-else 反而更清晰。我见过有人把简单的状态判断也套上策略模式,结果代码绕来绕去,维护成本反而高了。

3. 观察者模式:监听器想加就加,发布者浑然不觉

观察者模式解决的是「一对多通知」的问题。发布者不关心谁在监听,监听者也不影响发布者的逻辑。这种松耦合的设计,在事件驱动系统中随处可见。

我参与过一个实时监控系统,需要监控 CPU、内存、磁盘、网络等多个指标。每个指标变化时,要通知告警模块、日志模块、展示模块。如果用硬编码的方式,每加一个监控项就要改通知逻辑——那简直是灾难。

观察者模式的典型实现

// Java 示例:事件监听
public interface EventListener {
    void onEvent(String eventType, Object data);
}

public class EventManager {
    private Map<String, List<EventListener>> listeners = new HashMap<>();
    
    public void subscribe(String eventType, EventListener listener) {
        listeners.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener);
    }
    
    public void unsubscribe(String eventType, EventListener listener) {
        List<EventListener> list = listeners.get(eventType);
        if (list != null) {
            list.remove(listener);
        }
    }
    
    public void notify(String eventType, Object data) {
        List<EventListener> list = listeners.get(eventType);
        if (list != null) {
            for (EventListener listener : list) {
                listener.onEvent(eventType, data);
            }
        }
    }
}

// 使用示例
EventManager manager = new EventManager();
manager.subscribe("cpu_high", data -> System.out.println("告警:" + data));
manager.subscribe("cpu_high", data -> logService.record(data));
manager.notify("cpu_high", "CPU使用率95%");

扩展性要点:新增监听器时,只需要调用 subscribe 方法注册。EventManager 和发布者代码完全不需要修改。想加多少个监听器都行,互不影响。

4. 三种模式的对比与选择

模式 解决的问题 扩展方式 典型场景
工厂模式 对象创建 新增产品类 + 注册 商品类型、数据库驱动、日志输出
策略模式 算法选择 新增策略类 + 注入 折扣计算、排序算法、支付方式
观察者模式 事件通知 新增监听器 + 订阅 监控告警、消息推送、状态变更

这三种模式有一个共同点:都遵循开闭原则。对扩展开放,对修改关闭。新增功能时,尽量不修改已有代码,而是通过扩展来实现。

5. 实战中的组合使用

在实际项目中,这三种模式经常组合使用。我举个例子:

假设我们要做一个支付系统。支付方式有很多种(微信、支付宝、银联),每种支付方式有不同的处理流程(策略模式)。支付完成后,需要通知多个系统(订单系统、积分系统、消息推送)——这就是观察者模式。而创建支付对象的过程,可以用工厂模式来管理。

// 组合使用的伪代码
// 1. 工厂模式创建支付策略
PaymentStrategy strategy = PaymentFactory.create("wechat");

// 2. 策略模式执行支付
PaymentResult result = strategy.pay(order);

// 3. 观察者模式通知各方
EventManager.notify("payment_success", result);

我的建议:不要为了用模式而用模式。先问自己三个问题:

  1. 这个地方未来会不会经常变化?
  2. 变化的是创建逻辑、算法逻辑,还是通知逻辑?
  3. 引入模式后,代码是更清晰了还是更复杂了?

如果答案都是正向的,那就大胆用。

6. 知识体系总览

下面这张图展示了三种模式在扩展性方面的核心逻辑:

设计模式与扩展性:三种模式的核心逻辑 工厂模式 新产品接入 ProductFactory create(type) → Product 新增产品类 + 注册 调用方代码不变 开闭原则 ✓ 策略模式 新算法替换 Context setStrategy(s) 新增策略类 + 注入 业务逻辑不变 开闭原则 ✓ 观察者模式 新监听注册 EventManager notify(event, data) 新增监听器 + 订阅 发布者代码不变 开闭原则 ✓ 核心思想:对扩展开放,对修改关闭

你看,这三种模式虽然解决的问题不同,但背后的思想是一致的:把变化的部分封装起来,让稳定的部分不受影响。工厂模式封装了对象创建,策略模式封装了算法实现,观察者模式封装了通知逻辑。

嗯,最后说一句:设计模式不是银弹。但如果你能把这三种模式用好,你的系统扩展性至少能提升一个档次。我在实际项目中,80%的扩展需求都是用这三种模式解决的。


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