zoukankan      html  css  js  c++  java
  • 观察者模式

    具体场景为:气象站提供了一个WeatherData对象,该对象可以追踪获取天气的温度、气压、湿度信息,WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。

    1.    基本需求:利用WeatherData对象获取数据、并更新三个布告板:目前状况、气象统计和天气预报

    WeatherData类图如下:

     

    说明:

         GetTemperature()、GetHumidity()、GetPressure()分别用来获取天气温度、湿度气压,MeasurementsChanged()当气象测量更新此方法会被调用。

    分析:

    1. 可以通过WeatherData的Getter方法获取三个测量值:温度、湿度、气压
    2. 气象测量更新时会调用MeasurementsChanged()方法
    3. 需要实现三个天气数据布告板:“目前状况”、“气象统计”、“天气预报”,一旦WeatherData测到新值,这些布告也马上更新
    4. 此系统可扩展,可以随意的添加或者删除任何布告板。

    实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class WeatherData
    {
        public void MeasurementsChanged()
        {
            float temp = GetTemperature();
            float humidity = GetHumidity();
            float pressure = GetPressure();
            currentConditionDisplay.update(temp, humidity, pressure);
            statisticsDisplay.update(temp, humidity, pressure);
            forecastDisplay.update(temp, humidity, pressure);
        }
        //其他方法
    }

    反思:

    我们的这种实现有何不妥?

    结合我们第一节中的一些面向对象的原则,这种实现方式会有如下问题:

    l  针对具体实现编程,后续新增或者删除布告板必须修改程序

    l  未将改变的地方封装起来

    这种实现方式与面向对象的一些基本原则是相违背的,目前暂时是实现了用户的需求,但是在后续不具备可扩展行。

    2.    引入观察者模式

    2.1 出版者+订阅者=观察者模式

    出版者:就相当于“主题”(Subject),订阅者相当于“观察者”(Observer)

    当出版者(主题)发行新的报纸的时候,所有的观察者(订阅者)就可以收到最新的报纸,同时,当新的观察者(订阅者)加入时,也可以收到最新的报纸,当观察者(订阅者)退订报纸后,就再也收不到新的报纸。

    2.2 定义观察者模式

    观察者模式定义了对象之间一对多依赖,这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。

    主题和观察者定义一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知,根据通知的风格,观察者可能因此新值而更新。

    观察者模式:类图

     

    2.3设计原则:为了交互对象之间的松耦合设计而努力

    松耦合的威力

    l  当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。

    l  观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

    说明:

    1. 主题只知道观察者实现了某个接口(IObserver接口),不需要知道观察者是谁,或其他细节。
    2. 任何时候都可以增加或者删除的观察者,主题唯一依赖的是一个实现了IObserver接口的对象列表。
    3. 新的类型观察者出现时,主题代码不需要修改,只需要在新类型里实现观察者接口,然后注册为观察者即可。
    4. 可以独立的复用主题或观察者,因为二者松耦合。
    5. 改变主题或者观察者,并不会影响另一方。因为二者松耦合。

    松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖讲到了最低。

    3.    利用观察者模式设计并实现气象站

    3.1 设计气象站

    类图:

     

    3.2具体实现

    3.2.1主题、观察者、显示接口
    复制代码
    /// Description:对象、观察者、显示接口
        /// </summary>
        public interface ISubject
        {
            void RegisterObserver(IObserver o);//注册观察者
            void RemoveObserver(IObserver o);//删除观察者
            void NotifyObervers();//通知观察者
        }
     
        public interface IObserver
        {
            void Update(float temp, float humidity, float pressure);
        } 
    
        public interface IDisplayElement
        {
            void Display();
        }
    复制代码
    3.2.2 WeatherData类:注册、删除、通知观察者
    复制代码
        /// Description:WeatherData 注册、删除、通知观察者
        /// </summary>
        public class WeatherData:ISubject
        {
            private ArrayList observers;
            private float temperature;
            private float humidity;
            private float pressure;
    
            public WeatherData()
            {
                observers = new ArrayList();//初始化obervers,用来存储注册的观察者
            }
    
            /// <summary>
            /// 注册观察者
            /// </summary>
            /// <param name="o"></param>
            public void RegisterObserver(IObserver o)
            {
                observers.Add(o);
            }
    
           /// <summary>
           /// 删除观察者
            /// </summary>
            /// <param name="o"></param>
            public void RemoveObserver(IObserver o)
           {
                int i = observers.IndexOf(o);
                if (i >= 0)
                    observers.Remove(o);
            }
    
            /// <summary>
            /// 通知观察者
            /// </summary>
            public void NotifyObervers()
            {
                foreach (IObserver o in observers)
                {
                    o.Update(temperature, humidity, pressure);
                }
            }
    
            /// <summary>
            /// 当从气象站得到更新观测值时,通知观察者
            /// </summary>
            public void MeasurementsChanged()
            {
                NotifyObervers();
            }
    
    
            /// <summary>
            ///
            /// </summary>
            /// <param name="temperature"></param>
            /// <param name="humidity"></param>
           /// <param name="pressure"></param>
            public void SetMeasurements(float temperature, float humidity, float pressure)
            {
                this.temperature = temperature;
                this.humidity = humidity;
                this.pressure = pressure;
                MeasurementsChanged();
            }
        }
    复制代码
    3.2.3 布告板类,实现了IObserver、IDisplayElement接口

     

    复制代码
        /// Description:创建布告板
        /// </summary>
        public class CurrentConditionsDisplay:IObserver,IDisplayElement
        {
            private float temperature;
            private float humidity;
            private ISubject weatherData;
    
            public CurrentConditionsDisplay(ISubject weatherData)
            {
                this.weatherData = weatherData;
                weatherData.RegisterObserver(this);
            }
    
            public void Update(float temperature, float humidity, float pressure)
            {
                this.temperature = temperature;
                this.humidity = humidity;
                Display();
            }
            public void Display()
            {
                Console.WriteLine("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");
            }
        }
    复制代码
    3.2.4 测试
    WeatherStation.WeatherData weatherData = new WeatherStation.WeatherData();
    WeatherStation.CurrentConditionsDisplay currentDisplay = new WeatherStation.CurrentConditionsDisplay(weatherData);
    weatherData.SetMeasurements(10, 20, 30);

    结果如下:

     

    4.    Java内置的观察者模式

    Java内置的观察者模式,许多功能都已经事先准备好了,甚至可以用推(push)或拉(pull)的方式传送数据。

    使用java内置观察者模式实现气象站的OO设计类图,如下:

     

    Java内置观察者模式与我们在3小节中明显的差异是WeatherData继承自Observable类,并集成了一些增加、删除、通知观察者的方法。

    l  将对象变成观察者

    首先还是要实现Observer(观察者)接口,其次调用Observable对象的addObserver()方法即可。

    l  可观察者(主题)送出通知

    1. 先调用setChanged()方法,标记状态已经改变的事实。
    2. 调用notifyObservers()方法(该方法有2个,任意一个皆可):notifyObservers()或notifyObservers(Object arg)

    l  观察者接收通知

    观察者实现了Observer的接口,方法签名如下:

    update(Observable o,Object arg)

    第一个参数为主题本身,让观察者知道是哪个主题通知它的

    第二个参数是传入notifyObservers()的数据对象

    如果想用“推”的方式将数据给观察者,则可以把数据当做数据对象的方式传给notifyObservers(arg)

    如果想用“拉”的方式将数据给观察者,则需要在update()中,通过WeatherData的getTemperature()等方法获取对应的气象值。

    缺陷:

    Java内置的观察者模式中Observable是一个类,你必须设计一个类去继承它。如果某类相同时具有Observable类和另一个超类的行为,就无法实现,因为java不支持多继承。

    同时也限制了Observable的复用能力。

    同时,Observable API将setChanged()方法保护了起来,除非继承自Observable类,否则无法创建Observable实例组合到自己的对象中,也违背了面向对象设计的第二个原则:多用组合,少用继承。

    5.    总结

    l  OO原则:

    封装变化

    多用组合,少用继承

    针对接口编程,不针对实现编程

    对交互对象之间的松耦合设计而努力(新的OO原则,松耦合的设计更有弹性,更能应对变化)

    l  OO模式:

    观察者模式—在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

  • 相关阅读:
    day25:接口类和抽象类
    vue1
    How the weather influences your mood?
    机器学习实验方法与原理
    How human activities damage the environment
    Slow food
    Brief Introduction to Esports
    Massive open online course (MOOC)
    Online learning in higher education
    Tensorflow Dataset API
  • 原文地址:https://www.cnblogs.com/lgx5/p/7750338.html
Copyright © 2011-2022 走看看