24、三种模式整合(三):观察者模式监听工厂生产事件

好,我们继续往前走。

前面两章,我们把单例工厂搭好了,产品也能按需生成了。但有个问题——生产完了,然后呢?

在实际项目中,产品生产出来往往不是终点。比如订单系统里,商品入库后要通知库存模块更新、通知物流模块准备发货、通知财务模块记账……这些动作如果都写在工厂的createProduct()方法里,那工厂就变成了一锅粥。

我记得有一次接手一个老项目,工厂方法里塞了七八个业务回调,每次新增一个通知方,就得改工厂代码。改一次,测试回归一次,烦得很。后来我重构时引入了观察者模式,世界清净了。

观察者模式:让工厂学会“广播”

观察者模式的核心思想很简单:当一个对象状态发生变化时,自动通知所有依赖它的对象

说白了,就是“发布-订阅”。工厂只管生产,生产完了喊一嗓子:“产品出来了!”谁想听,谁就自己注册一下。

核心角色:

  • Subject(主题):工厂,负责维护观察者列表,生产时通知
  • Observer(观察者):各个业务模块,收到通知后执行自己的逻辑
  • Event(事件):生产事件,携带产品信息

先定义事件和观察者接口

我习惯先定义事件类,把生产出来的产品信息封装好。

// 生产事件
public class ProductionEvent {
    private final String productType;
    private final String productId;
    private final long timestamp;

    public ProductionEvent(String productType, String productId) {
        this.productType = productType;
        this.productId = productId;
        this.timestamp = System.currentTimeMillis();
    }

    // getters...
}

观察者接口也很简单,就一个方法:

public interface ProductionObserver {
    void onProductCreated(ProductionEvent event);
}

你想想看,这样设计的好处是什么?解耦。工厂不需要知道谁在监听,观察者也不需要知道工厂内部怎么生产的。双方只通过ProductionEvent这个契约沟通。

改造工厂:加入观察者管理

现在我们把单例工厂改造一下,让它支持观察者注册和通知。

public class ProductFactory {
    private static volatile ProductFactory instance;
    private final Map<String, Product> productCache = new ConcurrentHashMap<>();
    private final List<ProductionObserver> observers = new CopyOnWriteArrayList<>();

    private ProductFactory() {}

    public static ProductFactory getInstance() {
        if (instance == null) {
            synchronized (ProductFactory.class) {
                if (instance == null) {
                    instance = new ProductFactory();
                }
            }
        }
        return instance;
    }

    // 注册观察者
    public void addObserver(ProductionObserver observer) {
        observers.add(observer);
    }

    // 移除观察者
    public void removeObserver(ProductionObserver observer) {
        observers.remove(observer);
    }

    // 生产产品(核心方法)
    public Product createProduct(String type, String id) {
        Product product = null;
        switch (type) {
            case "A":
                product = new ConcreteProductA(id);
                break;
            case "B":
                product = new ConcreteProductB(id);
                break;
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }

        productCache.put(id, product);

        // 通知所有观察者
        ProductionEvent event = new ProductionEvent(type, id);
        notifyObservers(event);

        return product;
    }

    private void notifyObservers(ProductionEvent event) {
        for (ProductionObserver observer : observers) {
            observer.onProductCreated(event);
        }
    }
}

小提示:这里我用的是CopyOnWriteArrayList,而不是普通的ArrayList。为什么?因为观察者列表可能被多个线程同时读写,CopyOnWriteArrayList在遍历时不会抛ConcurrentModificationException。嗯,多线程环境下的坑,我踩过不少。

实现具体的观察者

假设我们有三个业务模块需要监听生产事件:

// 库存观察者
public class InventoryObserver implements ProductionObserver {
    @Override
    public void onProductCreated(ProductionEvent event) {
        System.out.println("[库存] 产品 " + event.getProductId() + " 已入库,更新库存数量");
        // 实际业务:更新数据库库存表
    }
}

// 物流观察者
public class LogisticsObserver implements ProductionObserver {
    @Override
    public void onProductCreated(ProductionEvent event) {
        System.out.println("[物流] 产品 " + event.getProductId() + " 待发货,生成运单");
        // 实际业务:调用物流API
    }
}

// 财务观察者
public class FinanceObserver implements ProductionObserver {
    @Override
    public void onProductCreated(ProductionEvent event) {
        System.out.println("[财务] 产品 " + event.getProductId() + " 已生产,记录成本");
        // 实际业务:生成财务凭证
    }
}

组装起来:客户端怎么用

public class Application {
    public static void main(String[] args) {
        ProductFactory factory = ProductFactory.getInstance();

        // 注册观察者
        factory.addObserver(new InventoryObserver());
        factory.addObserver(new LogisticsObserver());
        factory.addObserver(new FinanceObserver());

        // 生产产品——观察者自动收到通知
        factory.createProduct("A", "P001");
        factory.createProduct("B", "P002");
    }
}

运行结果:

[库存] 产品 P001 已入库,更新库存数量
[物流] 产品 P001 待发货,生成运单
[财务] 产品 P001 已生产,记录成本
[库存] 产品 P002 已入库,更新库存数量
[物流] 产品 P002 待发货,生成运单
[财务] 产品 P002 已生产,记录成本

你看,工厂每生产一个产品,三个观察者自动执行各自的逻辑。如果以后要加一个“质检观察者”,只需要新增一个类实现ProductionObserver,然后注册进去就行。工厂代码一行都不用改。

三种模式整合后的架构图

下面这张图展示了单例、工厂、观察者三种模式如何协同工作:

三种模式整合架构图 ProductFactory 单例模式 + 工厂模式 Product (产品) InventoryObserver LogisticsObserver FinanceObserver ProductionEvent ProductionEvent ProductionEvent addObserver() addObserver() addObserver() 工厂生产产品 → 自动通知所有已注册的观察者

避坑指南

我曾经踩过的坑:

  • 观察者注册时机:如果在工厂初始化之前就注册观察者,没问题。但如果在生产过程中动态注册,要注意线程安全问题。我建议所有观察者在系统启动阶段统一注册。
  • 观察者执行顺序:默认情况下,观察者按注册顺序执行。如果某个观察者执行耗时操作(比如调用外部API),会阻塞其他观察者。我一般会引入异步通知机制,或者用线程池来执行。
  • 内存泄漏:观察者注册后如果不及时移除,会导致工厂持有观察者的引用,垃圾回收无法回收。特别是在Web应用中,每次请求都注册观察者的话,内存会慢慢涨上去。

什么时候该用这种整合?

场景 推荐使用 原因
订单创建后需要通知多个系统 ✅ 强烈推荐 解耦订单服务和各个下游系统
数据导入完成后触发多个处理流程 ✅ 推荐 避免导入逻辑和业务处理耦合
只有1-2个通知方,且不会变化 ⚠️ 可考虑简化 直接调用可能更简单
通知方数量动态变化 ✅ 必须使用 观察者模式天然支持动态增删

我个人习惯是:只要生产事件有超过一个消费者,我就用观察者模式。哪怕现在只有两个,谁知道三个月后会不会变成五个?

好,这一章就到这里。三种模式整合的核心思路你已经掌握了:单例保证工厂唯一,工厂负责产品创建,观察者负责后续处理。各司其职,互不干扰。


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