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

4 阅读6分钟

一、设计模式概述

1.1 核心思想

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

1.2 UML类图结构

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

二、完整Java代码实现

2.1 基本实现

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
下载
复制
// 推模型 - 被观察者主动推送数据
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
下载
复制
// 线程安全观察者模式
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框架中的事件监听

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

3.2 消息中间件/事件总线

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
下载
复制
// 异步观察者模式
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 观察者优先级

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
下载
复制
// 使用弱引用包装观察者
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 性能优化建议

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 与中介者模式结合

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框架中的观察者

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中的观察者模式

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. 在分布式场景下考虑消息中间件替代

**