HeadFirst设计模式(2)-观察者模式(ObserverPattern)

第二节观察者模式(OberverPattern)

 HeadFirst这节给出的案例是气象站监测应用系统。背景是:气象站通过物流装置获取气象信息,生成WeatherData(气象数据),最后通过布告板显示给用户。WeatherData可以获取物理装置的数据,并且将数据显示到布告板上:目前状况(温度、湿度、气压)、气象统计和天气预报。

先给出一个错误示范

 public class WeatherData
    {
        public float GetTemperature()
        {
            return 25;
        }
        public float GetHumidity()
        {
            return 60;
        }

        public float GetPressure()
        {
            return 1;
        }

        public void MeasurementsChanged(){
            float temp = GetTemperature();
            float humidity = GetHumidity();
            float pressure = GetPressure();

            CurrentConditionsDisplay.Update(temp, humidity, pressure);
            StatisticsDisplay.Update(temp, humidity, pressure);
            ForecasDisplay.Update(temp, humidity, pressure);
        }
    }
View Code

这个代码示例中在更新布告板的时候,有两个问题:1、因为更新布告板都是调用了Update()方法,且参数都相同,这就违背了上一张策略模式中,需要将改变的地方封装起来。2、现在的布告板都是针对现实编程,如果要增加或删除布告板时就不要修改代码。

 我们先不急于解决这些问题,先了解一下观察者模式(Observer)。

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

对象通过主题接口注册为观察者或把自己从观察者中删除。一个具体的主题来实现主题接口。

利用观察者模式主题和观察者实现了之间的松耦合。使之直接可以交互,但是不清楚彼此的细节。

这里应用了一个设计原则--为交互对象之间的松耦合设计而努力

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

回到气象站的问题,我们用观察者模式来设计气象站系统,如下图所示类图。

 

 

这样我们来实现这些代码

气象站的接口及实现类

    public interface IWeatherData
    {
        /// <summary>
        /// 注册成为观察者
        /// </summary>
        /// <param name="o"></param>
        void RegisterObserver(IObserver o);
        /// <summary>
        /// 取消观察者
        /// </summary>
        /// <param name="o"></param>
        void RemoveObserver(IObserver o);
        /// <summary>
        /// 当数据更新是调用
        /// </summary>
        void NotifyObserver();
    }
View Code
 public class WeatherData:IWeatherData {
        private ArrayList Observers;
        private float temperature;
        private float humidity;
        private float pressure;
        public WeatherData()
        {
            this.Observers = new ArrayList();
        }
        public void RegisterObserver(IObserver o) {
            this.Observers.Add(o);
        }
        public void RemoveObserver(IObserver o)
        {
            this.Observers.Remove(o);
        }
        public void NotifyObserver()
        {
            foreach (IObserver item in Observers)
            {
                item.Update(this.temperature,this.humidity,this.pressure);
            }
        }

        public void MeasurementsChanged()
        {
            NotifyObserver();
        }

        public void SetMeasurements(float temp, float humidity, float pressure)
        {
            this.temperature = temp;
            this.humidity = humidity;
            this.pressure = pressure;
            MeasurementsChanged();
        }
    }
View Code

布告板的接口及实现类

   
   public interface IObserver
    {
        void Update(float temp, float humidity, float pressure);
    }


   public interface IDisplayElement
    {
        void Display();
    }
View Code
 public class CurrentConditionsDisplay:IObserver,IDisplayElement
    {
        private float temperature;
        private float humidity;
        private float pressure;
        public IWeatherData weatherData;
        public CurrentConditionsDisplay(IWeatherData weatherData)
        {
            this.weatherData = weatherData;
            weatherData.RegisterObserver(this);
        }
        public  void Update(float temp, float humidity, float pressure)
        {
            this.temperature = temp;
            this.humidity = humidity;
            this.pressure = pressure;
            Display();
        }

        public void Display()
        {
            Console.WriteLine(string.Format("气压版显示 温度:{0},湿度:{1}",this.temperature,this.humidity));
        }
    }
View Code

这样准备工作就做完了,开始执行

 WeatherData weatherData = new WeatherData();
            CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
           
            
            weatherData.SetMeasurements(80, 65, 30.4f);
View Code

同理,我们再将其他的观察者同样处理,修改完成后,在执行语句里面添加定义即可

WeatherData weatherData = new WeatherData();
            CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
            StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
            
            weatherData.SetMeasurements(80, 65, 30.4f);

OK执行结果达到了需求。

通过学习我们知道,当我们后面再要添加新的观察者或者要去掉以前的观察者时,不需要对WeatherData进行任何编辑,只需要把观察者的接口进行实现,并且在调用的时候注册即可。这样系统就非常有弹性。

 

posted @ 2015-05-05 09:53  西山农夫  阅读(256)  评论(0编辑  收藏  举报