2023-03-27
原文作者:ClarenceZero 原文地址:https://blog.csdn.net/ClarenceZero/article/details/106887075

观察者模式

定义

设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。

( 引用维基百科 )

观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。

观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。

( 引用百度百科 )

白话文

出版者+订阅者=观察者模式。主题对象管理某些数据,当主题内的数据改变,就会通知观察者。观察者已经订阅(注册)主题以便在主题数据改变时能够收到更新。

类图

202303272245262591.png

角色解析

  • Subject

    观察对象。定义了注册、删除、通知观察者接口。

  • ConcreteSubject

    观察对象具体实现类。

  • Observer

    所有潜在的观察者必须实现观察者接口。当主题状态改时它被调用。

  • ConcreteObserver

    具体的观察者可以是实现此接口的任意类。观察者必须注册到具体主题,以便接收更新。

实现

202303272245271222.png

1. 定义主题接口

    /**
     * 主题接口
     * 注册、通知、移除
     */
    public interface Subject {
        /**
         * 注册观察者
         * @param o
         */
        void registerObserver(Observer o);
    
        /**
         * 移除观察者
         * @param o
         */
        void removeObserver(Observer o);
    
        /**
         * 当主题状态改变时,这个方法会被调用,以通知所有的观察者
         */
        void notifyObservers();
    }

2. 主题接口实现类

    /**
     * 主题接口实现类
     */
    public class WeatherData implements Subject{
        /**
         * 温度
         */
        private float temperature;
        /**
         * 湿度
         */
        private float humidity;
        /**
         * 压力
         */
        private float pressure;
    
        /**
         * 观察者列表
         */
        private List<Observer> observerList;
        
        public WeatherData() {
            observerList = new ArrayList<>();
        }
    
        /**
         * 注册观察者对象
         * @param o
         */
        @Override
        public void registerObserver(Observer o) {
            observerList.add(o);
        }
    
        /**
         * 解除观察者对象
         * @param o
         */
        @Override
        public void removeObserver(Observer o) {
            observerList.remove(o);
        }
    
        /**
         * 通知所有观察者
         */
        @Override
        public void notifyObservers() {
            for (Observer observer : observerList) {
                observer.update(temperature, humidity, pressure);
            }
        }
    
        /**
         * 当测试值改变时,调用通知方法
         */
        public void measurementsChange() {
            notifyObservers();
        }
    
        /**
         * 此方法模拟测量值改变
         */
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChange();
        }
    }

3. 观察者接口

    /**
     * 定义观察者共同行为
     */
    public interface Observer {
        /**
         * 所有观察者必须实现接口。当主题值改变时,会调用此方法
         * @param temp
         * @param humidity
         * @param pressure
         */
        void update(float temp, float humidity, float pressure);
    }

4. 观察者具体实现类

    /**
     * 当前观察者实现类之一
     */
    public class CurrentConditionsDisplay implements Observer, DisplayElement{
        private float temperature;
        private float humidity;
        private Subject weatherData;
    
        public CurrentConditionsDisplay(Subject weatherData) {
            weatherData.registerObserver(this);
        }
    
        @Override
        public void display() {
            System.out.println("Current Condition: " + temperature + " F degrees and " + humidity + "% humidity.");
        }
    
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temperature = temp;
            this.humidity = humidity;
            display();
        }
    }
    /**
     * 当前观察者实现类之二
     */
    public class TempConditionDisplay implements Observer, DisplayElement{
        private float temp;
        public TempConditionDisplay(Subject subject) {
            subject.registerObserver(this);
        }
    
        @Override
        public void display() {
            System.out.println("Temp Condition Display,Current temp is " + temp);
        }
    
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temp = temp;
            display();
        }
    }

5. 其他接口及实现类

    /**
     * 布告板显示接口
     */
    public interface DisplayElement {
        void display();
    }

6. Main方法

    public class M {
        public static void main(String[] args) {
            // 创建主题类
            WeatherData weatherData = new WeatherData();
            // 创建观察者并把具体主题引入传入
            // 观察者之一
            CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
            // 观察者之二
            TempConditionDisplay tempConditionDisplay = new TempConditionDisplay(weatherData);
           
            // 温度变化
            weatherData.setMeasurements(1F, 3F, 4F);
        }
    }

总结

  1. 观察者模式提供了一种设计对象,让主题和观察者之间松耦合。主题只知道观察者实现了某个接口,并不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
  2. 任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表。可以动态新增、删除、替换观察者,而其他观察者不受影响。只要它们之间的接口仍被遵守,我们就可以自由改变它们。松耦合的设计能让我们建立弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
  3. 定义对象间的一种一对多的依赖关系。当一个对象状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  4. 观察者模式可以实现两种动作。
  5. 注意: 不要依赖于观察者被通知的次序。
  6. Java有内置的观察者模式。包含最基本的Observer接口与Observable。但是Observable是一个而非接口,限制了它的使用和复用。自Java1.9废弃。
         * This class and the {@link Observer} interface have been deprecated.
         * The event model supported by {@code Observer} and {@code Observable}
         * is quite limited, the order of notifications delivered by
         * {@code Observable} is unspecified, and state changes are not in
         * one-for-one correspondence with notifications.
         * For a richer event model, consider using the
         * {@link java.beans} package.  For reliable and ordered
         * messaging among threads, consider using one of the concurrent data
         * structures in the {@link java.util.concurrent} package.
         * For reactive streams style programming, see the
         * {@link java.util.concurrent.Flow} API.
  1. 如果一下被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,会触发它们之间的循环引用,进而导致系统崩溃。
阅读全文