观察者设计模式全方位深度解析

一、设计模式概述

1.1 核心思想

观察者模式(Observer Pattern)定义了对象间的一种一对多依赖关系,当一个对象(被观察者/主题)的状态发生改变时,所有依赖于它的对象(观察者)都会自动收到通知并更新

1.2 UML类图结构

scss 复制代码
复制
┌─────────────┐      ┌─────────────┐
│  Subject    │<---->│  Observer   │
├─────────────┤      ├─────────────┤
│+attach(obs) │      │+update()    │
│+detach(obs) │      └─────────────┘
│+notify()    │             △
└─────────────┘             |
       △                    |
       |              ┌─────┴─────┐
       |              │           │
┌─────────────┐  ┌─────────┐ ┌─────────┐
│ConcreteSubject│  │Concrete │ │Concrete │
│              │  │Observer1│ │Observer2│
├─────────────┤  ├─────────┤ ├─────────┤
│+getState()  │  │+update()│ │+update()│
│+setState()  │  └─────────┘ └─────────┘
└─────────────┘

二、完整Java代码实现

2.1 基本实现

csharp 复制代码
java
java
下载
复制
// 1. 观察者接口
interface Observer {
    void update();
}

// 2. 被观察者接口
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}

// 3. 具体被观察者实现
class WeatherStation implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;
    private float pressure;
    
    public float getTemperature() { return temperature; }
    public float getHumidity() { return humidity; }
    public float getPressure() { return pressure; }
    
    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();  // 数据变化时通知所有观察者
    }
    
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// 4. 具体观察者实现
class CurrentConditionsDisplay implements Observer {
    private WeatherStation weatherStation;
    
    public CurrentConditionsDisplay(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
        weatherStation.attach(this);
    }
    
    @Override
    public void update() {
        display();
    }
    
    public void display() {
        System.out.println("当前条件: 温度 " + weatherStation.getTemperature() 
            + "°C, 湿度 " + weatherStation.getHumidity() + "%");
    }
}

class StatisticsDisplay implements Observer {
    private WeatherStation weatherStation;
    private List<Float> temperatures = new ArrayList<>();
    
    public StatisticsDisplay(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
        weatherStation.attach(this);
    }
    
    @Override
    public void update() {
        temperatures.add(weatherStation.getTemperature());
        display();
    }
    
    public void display() {
        float avg = (float) temperatures.stream()
            .mapToDouble(Float::doubleValue)
            .average()
            .orElse(0.0);
        System.out.println("平均温度: " + avg + "°C");
    }
}

2.2 推模型 vs 拉模型实现

java 复制代码
java
java
下载
复制
// 推模型 - 被观察者主动推送数据
interface PushObserver {
    void update(float temp, float humidity, float pressure);
}

// 拉模型 - 观察者根据需要从被观察者拉取数据
interface PullObserver {
    void update(Subject subject);
}

// Java内置Observable实现(已过时,了解即可)
import java.util.Observable;
import java.util.Observer;

class WeatherDataOld extends Observable {
    private float temperature;
    
    public void measurementsChanged() {
        setChanged();  // 必须调用
        notifyObservers();  // 不传参数为拉模型
        // notifyObservers(temperature);  // 传参数为推模型
    }
}

2.3 线程安全实现

java 复制代码
java
java
下载
复制
// 线程安全观察者模式
class ThreadSafeSubject implements Subject {
    private final List<Observer> observers = new CopyOnWriteArrayList<>();
    private final Object lock = new Object();
    private volatile int state;
    
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
    
    public void setState(int newState) {
        synchronized(lock) {
            this.state = newState;
        }
        notifyObservers();
    }
}

三、应用场景分析

3.1 GUI框架中的事件监听

less 复制代码
java
java
下载
复制
// Java Swing中的观察者模式
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // 观察者响应事件
    }
});

3.2 消息中间件/事件总线

typescript 复制代码
java
java
下载
复制
// 简单事件总线实现
class EventBus {
    private Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();
    
    public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new ArrayList<>())
               .add((Consumer<Object>) handler);
    }
    
    public <T> void publish(T event) {
        List<Consumer<Object>> eventHandlers = handlers.get(event.getClass());
        if (eventHandlers != null) {
            eventHandlers.forEach(handler -> handler.accept(event));
        }
    }
}

3.3 实际应用场景

  1. MVC架构 - Model作为Subject,View作为Observer
  2. 配置管理 - 配置变更通知所有使用配置的组件
  3. 股票交易系统 - 股价变化通知所有相关方
  4. 发布-订阅系统 - 消息队列、Kafka等
  5. Reactive编程 - RxJava、Reactor等响应式框架

四、优缺点深度分析

4.1 优点

松耦合:观察者和被观察者之间依赖抽象,不依赖具体实现

开放封闭原则:易于增加新的观察者

广播通信:一对多通知效率高

运行时建立关系:观察关系可动态变更

4.2 缺点

通知顺序不确定:观察者接收通知的顺序可能不可控

内存泄漏风险:忘记移除观察者会导致对象无法回收

性能问题:观察者过多时通知耗时

循环依赖:观察者中又更新被观察者可能引发递归

更新细节不明确:观察者可能不知道具体什么发生了变化

五、改进与优化方向

5.1 异步通知优化

java 复制代码
java
java
下载
复制
// 异步观察者模式
class AsyncSubject implements Subject {
    private final ExecutorService executor = Executors.newFixedThreadPool(4);
    private final List<Observer> observers = new ArrayList<>();
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            executor.submit(() -> {
                try {
                    observer.update();
                } catch (Exception e) {
                    // 错误处理
                }
            });
        }
    }
}

5.2 观察者优先级

php 复制代码
java
java
下载
复制
// 带优先级的观察者模式
class PrioritySubject implements Subject {
    private final PriorityQueue<PriorityObserver> observers = 
        new PriorityQueue<>(Comparator.comparingInt(PriorityObserver::getPriority).reversed());
    
    class PriorityObserver {
        Observer observer;
        int priority;
        // getters, setters
    }
}

5.3 弱引用避免内存泄漏

java 复制代码
java
java
下载
复制
// 使用弱引用包装观察者
class WeakRefSubject implements Subject {
    private final List<WeakReference<Observer>> observers = new ArrayList<>();
    
    @Override
    public void attach(Observer observer) {
        observers.add(new WeakReference<>(observer));
    }
    
    @Override
    public void notifyObservers() {
        Iterator<WeakReference<Observer>> it = observers.iterator();
        while (it.hasNext()) {
            Observer observer = it.next().get();
            if (observer == null) {
                it.remove();  // 清理已被GC的观察者
            } else {
                observer.update();
            }
        }
    }
}

六、注意事项与最佳实践

6.1 线程安全注意事项

  • 在并发环境下使用CopyOnWriteArrayList或同步机制
  • 避免在通知过程中修改观察者列表
  • 考虑使用读写锁优化性能

6.2 性能优化建议

ini 复制代码
java
java
下载
复制
// 批量更新优化
class BatchUpdateSubject implements Subject {
    private boolean isNotifying = false;
    private boolean dirty = false;
    
    public void setState(Object newState) {
        this.state = newState;
        dirty = true;
        if (!isNotifying) {
            notifyObservers();
        }
    }
    
    @Override
    public void notifyObservers() {
        if (!dirty) return;
        
        isNotifying = true;
        dirty = false;
        // ... 通知逻辑
        isNotifying = false;
        
        if (dirty) {  // 通知期间又有新变化
            notifyObservers();
        }
    }
}

6.3 避免常见陷阱

  1. 在update()中修改Subject → 可能导致递归调用栈溢出
  2. 长耗时update() → 影响其他观察者,考虑异步
  3. 忘记detach() → 内存泄漏,使用弱引用或自动清理
  4. update()抛出异常 → 影响后续观察者,需要异常处理

七、与其他模式的关系

7.1 与发布-订阅模式区别

观察者模式 发布-订阅模式
直接通信 通过中间件通信
耦合度高 完全解耦
同步调用 支持异步
简单轻量 功能更强大

7.2 与中介者模式结合

typescript 复制代码
java
java
下载
复制
// 观察者+中介者混合模式
class EventMediator {
    private Map<String, List<Observer>> topicObservers = new HashMap<>();
    
    public void subscribe(String topic, Observer observer) {
        topicObservers.computeIfAbsent(topic, k -> new ArrayList<>())
                     .add(observer);
    }
    
    public void publish(String topic, Object data) {
        // 中介者负责路由消息
    }
}

八、实际应用示例

8.1 Spring框架中的观察者

typescript 复制代码
java
java
下载
复制
// Spring事件机制
@Component
class WeatherService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;
    
    public void setTemperature(float temp) {
        publisher.publishEvent(new TemperatureChangeEvent(this, temp));
    }
}

@Component
class TemperatureDisplay implements ApplicationListener<TemperatureChangeEvent> {
    @Override
    public void onApplicationEvent(TemperatureChangeEvent event) {
        System.out.println("温度变为: " + event.getTemperature());
    }
}

8.2 Android中的观察者模式

scala 复制代码
java
java
下载
复制
// LiveData观察者
public class WeatherViewModel extends ViewModel {
    private MutableLiveData<Float> temperature = new MutableLiveData<>();
    
    public LiveData<Float> getTemperature() {
        return temperature;
    }
}

// Activity中观察
viewModel.getTemperature().observe(this, newTemp -> {
    // UI更新
});

总结

观察者模式是一种极其重要的行为型设计模式,在事件驱动架构、响应式编程、UI框架等领域有广泛应用。正确使用时能够实现高度解耦的系统架构,但需要注意线程安全、内存管理、性能优化等实际问题。在现代开发中,通常使用成熟的框架实现(如Spring事件、RxJava等),而非手动实现,但深入理解其原理对于设计高质量软件至关重要。

关键建议

  1. 小型系统可手动实现简单观察者模式
  2. 企业级应用推荐使用成熟的事件框架
  3. 注意观察者的生命周期管理
  4. 考虑异步处理避免阻塞
  5. 在分布式场景下考虑消息中间件替代

**

相关推荐
杨充17 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充17 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充17 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充17 小时前
2.面向对象的特性
设计模式
杨充17 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充17 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充17 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯20 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式