观察者模式

内容分享7小时前发布
0 0 0

一、什么是观察者模式

23种设计模式
行为型
迭代器模式解释器模式观察者模式中介者模式访问者模式状态模式备忘录模式策略模式模板开发模式责任链模式命令模式
创建型
单例模式原型模式创造者模式工厂方法模式抽象工厂模式
结构型
桥接模式外观模式组合模式装饰器模式适配器模式代理模式享元模式

观察者模式属于行为型模式。在程序设计中,多个对象间存在一对多的依赖关系,当一个对象发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式。

二、组成

抽象被观察者(Subject):定义了一个接口,包含了注册观察者、删除观察者、通知观察者等方法。

具体被观察者(ConcreteSubject):实现了抽象被观察者接口,维护了一个观察者列表,并在状态发生改变时通知所有注册的观察者。

抽象观察者(Observer):定义了一个接口,包含了更新状态的方法。

具体观察者(ConcreteObserver):实现了抽象观察者接口,存储了需要观察的被观察者对象,并在被观察者状态发生改变时进行相应的处理。

三、应用场景

3.1 生活场景

  拍卖的时候,拍卖师是观察者,价格是被观察者。拍卖师观察最高标价,然后通知给其他竞价者竞价。

  共享单车:共享单车是被观察者对象,用户是观察者对象。当有新的单车被放置或被租用时,系统会发送给用户通知。

  微信公众号:微信公众号是被观察者对象,粉丝是观察者对象。当公众号发布了新的文章或消息时,系统会发送消息给关注该公众号的粉丝。

3.2 程序场景

  当一个对象的状态发生改变时,需要通知多个对象做出相应的响应。例如,游戏更新前,会通知所有用户要更新的时间。

  当很多对象同时对某一个主题感兴趣时,可以采用观察者模式实现发布-订阅模式。例如,生产者发送消息到消息队列中,并通知所有订阅此队列的消费者进行消费。

  数据库开发中,当数据库表中的数据发生变化时,需要通知相关的模块进行更新或其他操作。例如,当用户更新了数据库中的某个记录时,就可以通过观察者模式通知所有注册的监听器进行响应。

四、观察者模式实现

以下以“气象站数据监测系统”为例,实现观察者模式。该系统中,气象站(具体被观察者)收集温度、湿度、气压等数据,当数据更新时,需通知显示屏(具体观察者1)和数据记录仪(具体观察者2)进行同步更新。

4.2.1 抽象被观察者(Subject)




// 抽象被观察者接口
public interface Subject {
    // 注册观察者
    void registerObserver(Observer observer);
    // 移除观察者
    void removeObserver(Observer observer);
    // 通知所有观察者
    void notifyObservers();
}

4.2.2 具体被观察者(ConcreteSubject:气象站)



// 具体被观察者:气象站
public class WeatherStation implements Subject {
    
    private List<Observer> observers;
    // 温度、湿度、气压
    private float temperature;
    private float humidity;
    private float pressure;
 
    public WeatherStation() {
        observers = new ArrayList<>();
    }
 
    // 注册观察者
    @Override
    public void registerObserver(Observer observer) {
        if (!observers.contains(observer)) {
            observers.add(observer);
        }
    }
 
    // 移除观察者
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
 
    // 通知所有观察者
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            // 传递当前状态给观察者
            observer.update(temperature, humidity, pressure);
        }
    }
 
    // 模拟气象数据更新
    public void setWeatherData(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        // 数据更新后触发通知
        notifyObservers();
    }
}

4.2.3 抽象观察者(Observer)




// 抽象观察者接口
public interface Observer {
    // 接收通知并更新
    void update(float temperature, float humidity, float pressure);
}

4.2.4 具体观察者(ConcreteObserver: 显示屏,数据记录仪)




// 具体观察者1:显示屏
public class DisplayScreen implements Observer {
  
    private Subject weatherStation;
   
    private float temperature;
    private float humidity;
    private float pressure;
 
    // 构造方法中完成注册
    public DisplayScreen(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("【显示屏】实时气象数据:温度=" + temperature + "℃,湿度=" + humidity + "%,气压=" + pressure + "hPa");
    }
}




// 具体观察者2:数据记录仪
public class DataRecorder implements Observer {
    private Subject weatherStation;
    private float temperature;
    private float humidity;
    private float pressure;
 
    public DataRecorder(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;
        recordData();
    }
 
    // 数据存储
    public void recordData() {
        System.out.println("【数据记录仪】存储气象数据:温度=" + temperature + "℃,湿度=" + humidity + "%,气压=" + pressure + "hPa");
    }
}

4.2.5 测试类(客户端)




// 客户端测试
public class Test {
    public static void main(String[] args) {
        // 创建具体被观察者:气象站
        WeatherStation weatherStation = new WeatherStation();
 
        // 创建具体观察者:显示屏、数据记录仪(创建时自动注册)
        DisplayScreen displayScreen = new DisplayScreen(weatherStation);
        DataRecorder dataRecorder = new DataRecorder(weatherStation);
 
        // 模拟气象数据更新
        System.out.println("=== 第一次气象数据更新 ===");
        weatherStation.setWeatherData(35.5f, 60.0f, 1013.25f);
 
        System.out.println("
=== 第二次气象数据更新 ===");
        weatherStation.setWeatherData(36.3f, 58.5f, 1012.8f);
 
        // 移除观察者:显示屏
        weatherStation.removeObserver(displayScreen);
        System.out.println("
=== 移除显示屏后,第三次气象数据更新 ===");
        weatherStation.setWeatherData(37.0f, 55.0f, 1012.0f);
    }
}

4.2.6测试结果

观察者模式

五、特点

优点

降低耦合度:被观察者与观察者之间通过抽象接口交互,无需知道对方的具体实现,实现了松耦合。当需要新增或修改观察者时,无需修改被观察者的代码,符合开闭原则。

实现联动更新:支持一对多的依赖关系,当被观察者状态变化时,所有相关观察者可自动接收通知并更新,无需手动调用,提高了代码的自动化程度和维护效率。

动态灵活性高:观察者可以动态注册或移除,能够根据业务需求灵活调整依赖关系。例如,在上述气象站示例中,可随时添加新的观察者,无需修改气象站的核心逻辑。

缺点

通知效率问题:当观察者数量较多时,被观察者通知所有观察者的过程会消耗较多时间,可能导致系统响应延迟。尤其是在同步通知模式下,若某个观察者的update方法执行耗时较长,会阻塞后续观察者的通知。

循环依赖风险:若观察者与被观察者之间存在双向依赖(如:观察者状态变化后反过来触发被观察者状态更新),可能导致循环通知,引发系统异常或死循环。

通知顺序不确定:在默认实现中,观察者的通知顺序由其注册顺序决定,但该顺序并未在接口中明确约定,若观察者之间存在依赖关系(如:观察者A需在观察者B之前更新),可能导致业务逻辑错误。

内存泄漏隐患:若观察者在不需要接收通知时未被正确移除,被观察者会一直持有观察者的引用,导致观察者对象无法被垃圾回收,引发内存泄漏。

© 版权声明

相关文章

暂无评论

none
暂无评论...