一、引言

在软件开发的广阔天地中,设计模式 是解决特定问题的成熟方案。其中,观察者模式(Observer Pattern)作为行为型设计模式的重要成员,在构建松耦合、易扩展的系统架构中扮演着核心角色。

什么是观察者模式?

观察者模式 定义了对象间的一对多依赖关系,使得当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。这种模式又被称为 发布-订阅模式(Publish-Subscribe Pattern)。

核心作用

观察者模式的核心价值在于:

  • 实现松耦合:被观察者无需知道具体观察者的存在
  • 动态绑定:观察者可以随时注册或注销
  • 广播通信:一个事件可以通知多个监听器

学习价值

掌握观察者模式将帮助你:

  • 理解Spring事件机制、Vue响应式原理等框架核心思想
  • 构建可维护的大型应用系统
  • 提升代码的可测试性和扩展性

二、核心概念

模式结构

观察者模式包含以下核心角色:

1. Subject(被观察者/主题)

  • 职责:维护观察者列表,提供注册、删除和通知方法
  • 特点:状态变化时主动通知所有观察者

2. Observer(观察者)

  • 职责:定义更新接口,接收被观察者的通知
  • 特点:多个观察者可注册到同一被观察者

3. ConcreteSubject(具体被观察者)

  • 职责:实现Subject接口,存储具体状态

4. ConcreteObserver(具体观察者)

  • 职责:实现Observer接口,定义具体更新行为

工作原理流程图

┌─────────────────────────────────────────────────────────┐
│                    观察者模式工作流程                    │
└─────────────────────────────────────────────────────────┘

观察者A注册 ───┐
              ├──→ 被观察者 ──→ 状态变化 ──→ 通知所有观察者
观察者B注册 ───┘                             │
                                          ↓
                                    观察者A收到通知 → 更新状态
                                    观察者B收到通知 → 更新状态

三、实现方式

方式一:基于Java内置Observable和Observer

Java提供了内置的观察者模式支持,位于java.util包中。

完整代码示例

1. 创建被观察者类

import java.util.Observable;

/**
 * 天气站 - 被观察者
 * 继承Observable类,实现状态管理
 */
public class WeatherStation extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;
    
    /**
     * 测量数据更新
     * 模拟数据变化场景
     */
    public void measurementsChanged() {
        setChanged();  // 标记状态已改变
        notifyObservers();  // 通知所有观察者
    }
    
    /**
     * 设置测量数据
     * @param temperature 温度
     * @param humidity 湿度
     * @param pressure 气压
     */
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    // Getter方法
    public float getTemperature() { return temperature; }
    public float getHumidity() { return humidity; }
    public float getPressure() { return pressure; }
}

2. 创建观察者类

import java.util.Observable;
import java.util.Observer;

/**
 * 当前天气显示 - 观察者
 * 实现Observer接口,定义更新逻辑
 */
public class CurrentConditionsDisplay implements Observer {
    private Observable observable;
    private float temperature;
    private float humidity;
    
    /**
     * 构造函数,注册观察者
     * @param observable 被观察者对象
     */
    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);  // 注册为观察者
    }
    
    /**
     * 更新方法
     * 被观察者状态变化时自动调用
     */
    @Override
    public void update(Observable obs, Object arg) {
        if (obs instanceof WeatherStation) {
            WeatherStation weatherStation = (WeatherStation) obs;
            this.temperature = weatherStation.getTemperature();
            this.humidity = weatherStation.getHumidity();
            display();
        }
    }
    
    /**
     * 显示当前天气信息
     */
    public void display() {
        System.out.println("当前天气状况:");
        System.out.println("温度: " + temperature + "°C");
        System.out.println("湿度: " + humidity + "%");
        System.out.println("------------------------");
    }
}

3. 使用示例

/**
 * 观察者模式测试类
 */
public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建被观察者
        WeatherStation weatherStation = new WeatherStation();
        
        // 创建观察者并注册
        CurrentConditionsDisplay currentDisplay = 
            new CurrentConditionsDisplay(weatherStation);
        
        // 模拟数据变化
        System.out.println("第一次数据更新:");
        weatherStation.setMeasurements(26.5f, 65f, 1013.1f);
        
        System.out.println("第二次数据更新:");
        weatherStation.setMeasurements(28.3f, 70f, 1012.5f);
        
        System.out.println("第三次数据更新:");
        weatherStation.setMeasurements(24.8f, 58f, 1014.2f);
    }
}

输出结果:

第一次数据更新:
当前天气状况:
温度: 26.5°C
湿度: 65.0%
------------------------
第二次数据更新:
当前天气状况:
温度: 28.3°C
湿度: 70.0%
------------------------
第三次数据更新:
当前天气状况:
温度: 24.8°C
湿度: 58.0%
------------------------

注意:Java内置的Observable类在Java 9中已被标记为@Deprecated,官方推荐使用自定义实现方式。


方式二:基于接口编程的自定义实现

这是更加灵活和推荐的实现方式。

1. 定义观察者接口

/**
 * 观察者接口
 * 定义所有观察者必须实现的更新方法
 */
public interface Observer {
    /**
     * 更新方法
     * @param temperature 温度
     * @param humidity 湿度
     * @param pressure 气压
     */
    void update(float temperature, float humidity, float pressure);
}

2. 定义被观察者接口

/**
 * 被观察者接口
 * 定义注册、删除和通知观察者的方法
 */
public interface Subject {
    /**
     * 注册观察者
     * @param observer 观察者对象
     */
    void registerObserver(Observer observer);
    
    /**
     * 移除观察者
     * @param observer 观察者对象
     */
    void removeObserver(Observer observer);
    
    /**
     * 通知所有观察者
     */
    void notifyObservers();
}

3. 实现具体被观察者

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

/**
 * 自定义天气站实现
 * 实现Subject接口,管理观察者列表
 */
public class CustomWeatherStation implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    
    public CustomWeatherStation() {
        observers = new ArrayList<>();
    }
    
    @Override
    public void registerObserver(Observer observer) {
        if (observer != null && !observers.contains(observer)) {
            observers.add(observer);
        }
    }
    
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
    
    /**
     * 数据变化时调用
     */
    public void measurementsChanged() {
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

4. 实现具体观察者

/**
 * 自定义天气显示面板
 */
public class CustomWeatherDisplay implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherStation;
    
    public CustomWeatherDisplay(Subject weatherStation) {
        this.weatherStation = weatherStation;
        weatherStation.registerObserver(this);
    }
    
    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
    
    public void display() {
        System.out.println("=== 实时天气面板 ===");
        System.out.printf("温度: %.1f°C\n", temperature);
        System.out.printf("湿度: %.1f%%\n", humidity);
        System.out.printf("气压: %.1f hPa\n", pressure);
        System.out.println("===================");
    }
}

5. 测试代码

/**
 * 自定义观察者模式演示
 */
public class CustomObserverDemo {
    public static void main(String[] args) {
        // 创建被观察者
        CustomWeatherStation weatherStation = new CustomWeatherStation();
        
        // 创建多个观察者
        CustomWeatherDisplay display1 = new CustomWeatherDisplay(weatherStation);
        CustomWeatherDisplay display2 = new CustomWeatherDisplay(weatherStation);
        
        // 模拟数据更新
        System.out.println("=== 天气数据更新测试 ===\n");
        weatherStation.setMeasurements(25.5f, 60f, 1013.0f);
        
        // 移除一个观察者
        weatherStation.removeObserver(display2);
        System.out.println("\n=== 移除display2后的更新 ===\n");
        weatherStation.setMeasurements(27.0f, 62f, 1012.5f);
    }
}

四、应用场景分析

1. Spring框架中的应用

Spring框架大量使用了观察者模式:

Spring事件机制

/**
 * 自定义事件
 */
public class CustomEvent extends ApplicationEvent {
    private String message;
    
    public CustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    
    public String getMessage() {
        return message;
    }
}

/**
 * 事件监听器
 */
@Component
public class CustomEventListener {
    
    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("收到事件通知: " + event.getMessage());
    }
}

/**
 * 发布事件
 */
@Service
public class EventPublisherService {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void publishEvent(String message) {
        CustomEvent event = new CustomEvent(this, message);
        applicationContext.publishEvent(event);
    }
}

应用场景:

  • 用户注册成功后发送欢迎邮件
  • 订单状态变更通知
  • 缓存更新事件

2. Android开发中的应用

Android的事件监听机制:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 处理点击事件
    }
});

3. 实际项目案例

股票价格监控系统:

/**
 * 股票被观察者
 */
public class StockSubject implements Subject {
    private List<Observer> investors = new ArrayList<>();
    private double stockPrice;
    
    public void setStockPrice(double price) {
        this.stockPrice = price;
        notifyObservers();
    }
    
    @Override
    public void notifyObservers() {
        for (Observer investor : investors) {
            investor.update(stockPrice);
        }
    }
    
    // 其他方法实现...
}

/**
 * 投资者观察者
 */
public class Investor implements Observer {
    private String name;
    private double buyPrice;
    
    public Investor(String name, double buyPrice) {
        this.name = name;
        this.buyPrice = buyPrice;
    }
    
    @Override
    public void update(double currentPrice) {
        double profit = currentPrice - buyPrice;
        System.out.printf("投资者 %s: 当前股价 %.2f, 盈亏: %.2f\n", 
            name, currentPrice, profit);
    }
}

五、优缺点总结

优势分析

优势说明实际价值
松耦合被观察者和观察者之间抽象耦合提高代码可维护性
广播通信一对多的消息传递简化事件处理逻辑
动态扩展运行时增删观察者系统灵活性高
符合开闭原则新增观察者无需修改被观察者易于功能扩展

潜在缺点

  1. 内存泄漏风险

    • 观察者忘记注销会导致内存泄漏
    • 解决方案:在对象销毁时主动调用removeObserver()
  2. 执行顺序不确定性

    • 多个观察者的通知顺序不确定
    • 影响:某些业务场景需要特定执行顺序
  3. 性能考虑

    • 观察者过多可能影响通知效率
    • 建议:异步处理或使用线程池
  4. 调试困难

    • 事件驱动模式下调用链不直观
    • 解决方案:添加详细日志记录

代码示例:避免内存泄漏

/**
 * 观察者使用示例 - 正确的资源释放
 */
public class ObserverUsageExample implements AutoCloseable {
    private Subject weatherStation;
    private Observer observer;
    
    public ObserverUsageExample() {
        weatherStation = new CustomWeatherStation();
        observer = new CustomWeatherDisplay(weatherStation);
    }
    
    @Override
    public void close() {
        // 重要:使用完毕后注销观察者
        if (weatherStation != null && observer != null) {
            weatherStation.removeObserver(observer);
            System.out.println("观察者已注销,避免内存泄漏");
        }
    }
}

六、与其他模式对比

观察者模式 vs 发布-订阅模式

虽然经常被混淆,但两者有明显区别:

对比维度观察者模式发布-订阅模式
耦合度观察者与被观察者直接依赖通过消息中间件间接通信
通信方式同步调用异步消息传递
扩展性相对有限更好的扩展性
典型应用Java内置Observer、Spring事件Kafka、RabbitMQ消息队列

发布-订阅模式示例

/**
 * 简化的发布-订阅模式(使用消息队列概念)
 */
public class MessageBroker {
    private Map<String, List<Subscriber>> topicMap = new HashMap<>();
    
    public void subscribe(String topic, Subscriber subscriber) {
        topicMap.computeIfAbsent(topic, k -> new ArrayList<>())
               .add(subscriber);
    }
    
    public void publish(String topic, String message) {
        List<Subscriber> subscribers = topicMap.get(topic);
        if (subscribers != null) {
            for (Subscriber subscriber : subscribers) {
                subscriber.onMessage(message);
            }
        }
    }
}

interface Subscriber {
    void onMessage(String message);
}

与其他设计模式的关系

  1. 中介者模式:观察者模式是一对多,中介者模式是多对多协调
  2. 责任链模式:观察者模式是广播,责任链是链式传递
  3. MVC模式:Model和View之间常使用观察者模式

七、最佳实践

1. 设计建议

✅ 推荐做法

/**
 * 良好的观察者模式设计
 */
public interface EventObserver {
    /**
     * 使用泛型提高类型安全性
     */
    <T> void onEvent(T event);
}

/**
 * 被观察者使用线程池异步通知
 */
public class AsyncSubject implements Subject {
    private ExecutorService executor = Executors.newFixedThreadPool(10);
    private List<Observer> observers = new CopyOnWriteArrayList<>();
    
    @Override
    public void notifyObservers() {
        // 异步通知,避免阻塞主线程
        for (Observer observer : observers) {
            executor.submit(() -> observer.update(null));
        }
    }
    
    public void shutdown() {
        executor.shutdown();
    }
}

❌ 避免的做法

// 不要在update方法中执行耗时操作
@Override
public void update(float temperature, float humidity, float pressure) {
    // 错误:阻塞主线程的耗时操作
    try {
        Thread.sleep(5000);  // 不要这样做!
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2. 使用注意事项

  1. 线程安全性

    • 使用CopyOnWriteArrayList或同步机制保护观察者列表
    • 避免在通知过程中修改观察者列表
  2. 异常处理

    • 单个观察者的异常不应影响其他观察者
    • 建议在update方法中添加try-catch
@Override
public void notifyObservers() {
    for (Observer observer : observers) {
        try {
            observer.update(data);
        } catch (Exception e) {
            // 记录异常,但继续通知其他观察者
            System.err.println("观察者更新失败: " + e.getMessage());
        }
    }
}
  1. 性能优化
    • 对于高频事件,考虑使用事件聚合
    • 使用异步处理提升性能

3. 实际项目应用模板

/**
 * 企业级观察者模式模板
 */
public class EventBusTemplate {
    // 事件注册表
    private Map<Class<?>, List<EventHandler>> handlers = new ConcurrentHashMap<>();
    
    /**
     * 注册事件处理器
     */
    public <T> void register(Class<T> eventType, EventHandler<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
               .add(handler);
    }
    
    /**
     * 发布事件
     */
    public <T> void publish(T event) {
        List<EventHandler> eventHandlers = handlers.get(event.getClass());
        if (eventHandlers != null) {
            eventHandlers.forEach(handler -> handler.handle(event));
        }
    }
    
    /**
     * 事件处理器接口
     */
    @FunctionalInterface
    public interface EventHandler<T> {
        void handle(T event);
    }
}

八、总结

观察者模式作为经典的设计模式,在Java开发生态中占据重要地位。通过本文的学习,我们了解到:

核心要点回顾:

  • ✅ 掌握观察者模式的定义和核心角色
  • ✅ 了解两种实现方式及其适用场景
  • ✅ 理解在Spring等框架中的实际应用
  • ✅ 认识优缺点并掌握最佳实践

进阶学习建议:

  1. 深入学习Spring事件机制源码
  2. 研究Reactive Programming响应式编程
  3. 了解消息队列的实现原理

适用场景判断:
当需要实现对象间的一对多依赖、解耦核心逻辑与副作用处理、实现事件驱动架构时,观察者模式都是理想的选择。

更多推荐