设计模式:观察者模式

简介: 观察者模式属于行为型设计模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变化时,所有依赖的观察者(Observer)会自动收到通知并更新。

一、模式定义

观察者模式属于行为型设计模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变化时,所有依赖的观察者(Observer)会自动收到通知并更新。

二、核心角色

  1. Subject(主题)

    • 维护观察者列表,提供添加/删除观察者的方法
    • 定义通知观察者的方法
  2. Observer(观察者接口)

    • 定义更新接口,用于接收主题通知
  3. ConcreteSubject(具体主题)

    • 存储具体状态信息
    • 状态改变时触发通知
  4. ConcreteObserver(具体观察者)

    • 实现更新逻辑,保持与主题状态同步

三、经典实现(以气象站为例)

// 1. 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 2. 观察者接口
interface Observer {
    void update(float temp, float humidity, float pressure);
}

// 3. 具体主题实现
class WeatherData implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;
    private float pressure;

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(temperature, humidity, pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

// 4. 具体观察者实现
class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Subject weatherData) {
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        display();
    }

    public void display() {
        System.out.println("Current conditions: " + temperature 
            + "°C and " + humidity + "% humidity");
    }
}

// 5. 使用示例
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay display = new CurrentConditionsDisplay(weatherData);

        weatherData.setMeasurements(25.5f, 65, 1013.1f);
        weatherData.setMeasurements(26.8f, 70, 1012.5f);
    }
}

四、两种通知模型对比

特性 推模型(Push) 拉模型(Pull)
数据传递方式 主题主动发送完整数据 观察者从主题拉取所需数据
实现复杂度 观察者可能收到不需要的数据 观察者按需获取数据,需要维护主题引用
耦合度 观察者依赖具体数据结构 观察者只需知道主题存在
性能考量 可能传输冗余数据 需要多次调用获取方法

拉模型改进示例:

interface Subject {
    // 新增获取状态的方法
    float getTemperature();
    float getHumidity();
    float getPressure();
}

class WeatherData implements Subject {
    // 保持原有接口不变
    // 新增状态获取方法...
}

class CurrentConditionsDisplay implements Observer {
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void update() {
        this.temperature = weatherData.getTemperature();
        this.humidity = weatherData.getHumidity();
        display();
    }
}

五、Java内置支持

// 已过时,不推荐使用,仅作了解
import java.util.Observable;
import java.util.Observer;

class WeatherData extends Observable {
    public void measurementsChanged() {
        setChanged();  // 必须调用
        notifyObservers();  // 无参为拉模型
        // notifyObservers(data);  // 带参为推模型
    }
}

class Display implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            // 拉取数据
            WeatherData wd = (WeatherData) o;
            // 或使用arg参数获取推送数据
        }
    }
}

六、模式优劣分析

优势:

  • 符合开闭原则:新增观察者无需修改主题
  • 运行时动态建立对象关系
  • 实现抽象耦合,主题无需知道具体观察者

劣势:

  • 通知顺序不可控
  • 频繁更新可能影响性能
  • 循环依赖风险

七、应用场景

  1. 跨系统事件通知(如订单状态更新)
  2. GUI事件处理(按钮点击监听)
  3. 实时数据监控(股票价格变动)
  4. 游戏中的成就系统解锁
  5. 分布式配置中心(配置变更通知)

八、高级应用技巧

  1. 异步观察者
    使用线程池处理通知,避免阻塞主题线程
ExecutorService executor = Executors.newCachedThreadPool();

void notifyObservers() {
    for (Observer o : observers) {
        executor.execute(() -> o.update(...));
    }
}
  1. 防止失效监听
    使用弱引用(WeakReference)存储观察者,防止内存泄漏
  2. 保证通知顺序
    使用PriorityQueue实现带优先级的观察者队列
  3. 跨进程观察者
    结合消息队列(如RabbitMQ、Kafka)实现分布式观察者模式

九、相关模式对比

  1. 中介者模式 vs 观察者模式
    中介者集中处理对象间通信,而观察者建立直接订阅关系
  2. 发布-订阅模式 vs 观察者模式
    发布-订阅通过消息代理解耦,观察者是直接通信

十、最佳实践建议

  1. 优先使用拉模型,降低耦合度
  2. 为观察者接口设计合适的更新粒度
  3. 考虑使用CopyOnWriteArrayList保证线程安全
  4. 对于复杂场景,建议使用Guava的EventBus或Spring事件机制
相关文章
|
5月前
|
设计模式 监控 Java
Kotlin - 改良设计模式 - 观察者模式
Kotlin - 改良设计模式 - 观察者模式
79 3
|
4月前
|
设计模式 存储 供应链
前端必须掌握的设计模式——观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,实现了一种订阅机制。它包含两个角色:**观察者**(订阅消息、接收通知并执行操作)和**被观察者**(维护观察者列表、发送通知)。两者通过一对多的关系实现解耦,当被观察者状态改变时,会通知所有订阅的观察者。例如,商店老板作为被观察者,记录客户的需求并在商品到货时通知他们。前端应用中,如DOM事件注册、MutationObserver等也体现了这一模式。
|
5月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
6月前
|
设计模式 传感器
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
【设计模式】观察者模式(定义 | 特点 | Demo入门讲解)
102 0
|
5月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
106 6
|
5月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
63 1
|
5月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
49 3
|
6月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
51 9
|
6月前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
67 2
|
6月前
|
设计模式 监控 UED
设计模式之观察者模式
【10月更文挑战第12天】 观察者模式是一种行为型设计模式,定义了一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。主要由主题(被观察者)和观察者组成,实现对象间的松耦合,广泛应用于用户界面、事件驱动系统和数据监控等领域。

热门文章

最新文章

下一篇
oss创建bucket