模式设计连载三.doc

上传人:11****ws 文档编号:3208831 上传时间:2019-05-25 格式:DOC 页数:17 大小:191.38KB
下载 相关 举报
模式设计连载三.doc_第1页
第1页 / 共17页
模式设计连载三.doc_第2页
第2页 / 共17页
模式设计连载三.doc_第3页
第3页 / 共17页
模式设计连载三.doc_第4页
第4页 / 共17页
模式设计连载三.doc_第5页
第5页 / 共17页
点击查看更多>>
资源描述

1、气象站的故事-观察者模式前言大家好!好久没有更新这个系列的文章了,这两个来月回家过了新年,公司搬了新家,就是这个系列的文章还没有更新,实在惭愧!同时再次真诚地感谢一直喜欢和支持这个系列文章的朋友们,因为你们的鼓励,我才有动力继续下去。可能因为这个系列每篇文章都比较长的原因,为了保证质量我总是字斟句酌,所以每次想动笔的时候都有点胆怯的感觉,但是还好每次只要写了开头我就会坚持把它写完的,还是万事开头难啊。上篇【策略模式】得到了很多朋友的支持,倍感欣慰。这篇文章将延续以往的风格,故事来源同样取材于Head First Design Patterns,气象站的故事部分并非我原创,只是把原书的内容用更通

2、俗易懂的方式展现给大家,特此声明一下,而这里更多的是作者对模式本身的理解和扩展的引申。关于 HFDP,推荐大家去购买和阅读原版图书。OK!我们这就开始!(提示:西红柿和鸡蛋都是好东西,请不要乱丢)气象站的故事现在我们要为一家气象站开发一套气象监控系统,按照客户的要求,这个监控系统必须可以实时跟踪当前的天气状况(温度、湿度、大气压力),并且可以在三种不同设备上显示出来(当前天气状况、天气统计、天气预测)。客户还希望这个系统可以对外提供一个 API 接口,以便任何开发者都可以开发自己的显示设备,然后无缝挂接到系统中,系统可以统一更新所有显示设备的数据。客户还会提供一个可以访问气象站的硬件设备的组件

3、,如下图所示:它提供了三个方法(get 开头),可以分别取得实时的温度、湿度和大气压力,还有一个MeasurementsChanged()方法,当任何天气状况发生变化的时候,这个方法都会自动被触发,当前这个方法只是一个空函数,扩展的代码还需要我们自己去扩充。至于 WeatherData 是如何取得天气状况的,还有MeasurementsChanged()方法是如何被自动触发的这些事情都不需要我们去考虑,我们只管考虑如果做好跟显示设备有关的事情就好了。 OK!让我们来考虑一下这个系统的实现,先重新理一下思路:1 客户提供了获取实时的天气状况的方法。2 MeasurementsChanged()方

4、法会在天气状况变化时被自动调用。3 系统要实现三种显示模式,分别显示天气状况、天气统计和天气预测,而且这些显示的信息必须跟当前最新的天气状况实时同步。4 系统还必须支持在显示方式上的扩展性,而且使用者可以任意添加和移除不同的显示模式。基于上面这些信息,我们大概都会想到可以象下面这样来实现这个系统:/伪代码public class WeatherData /实例化显示设备(省略)public void MeasurementsChanged()float temp = getTemperature(); /取得温度float humidity = getHumidity(); /取得湿度floa

5、t pressure = getPressure(); /取得气压currentConditionsDisplay.update(temp, humidity, pressure); /同步显示当前天气状况statisticsDisplay.update(temp, humidity, pressure); /同步显示天气统计信息forecastDisplay.update(temp, humidity, pressure); /同步显示天气预报信息因为客户已经给我们提供了实时的数据,还提供了数据更新时候的触发机制,那么我们要做的就是把最新的数据提供给不同的显示设备就 OK 了,上面的代码好象

6、已经可以基本解决问题啦。哈哈!真的就这么简单就搞定了吗?让我们用上一篇【策略模式】里学习到的原则来审视一下这个实现。首先,xxxDisplay 这几个对象都是具体的类实例,也就是说我们在这里违背了“面向接口编程,而不要面向实现编程。”的原则,这样实现会带来的问题是系统无法满足在不修改代码的情况下动态添加或移除不同的显示设备。换句话说,显示设备相关的部分是系统中最不稳定的部分,应该将其单独隔离开,也就是前面学过的另一个原则:“找到系统中变化的部分,将变化的部分同其它稳定的部分隔开。”那么我们到底该怎么办呢?呵呵,既然这篇文章是讲观察者模式的,当然要用它来结束战斗!下面我们先来认识一下观察者模式这

7、就是观察者模式我们还是先看一下官方的定义:The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically. (观察者模式定义了对象间的一种一对多依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新)咋样?这是超级经典的标准定义,如假抱换的!不懂?那再看看下面的类图吧Subject(被观察的对象接

8、口) 规定 ConcreteSubject 的统一接口; 每个 Subject 可以有多个 Observer;ConcreteSubject(具体被观察对象) 维护对所有具体观察者的引用的列表; 状态发生变化时会发送通知给所有注册的观察者。Observer(观察者接口) 规定 ConcreteObserver 的统一接口; 定义了一个 update()方法,在被观察对象状态改变时会被调用。ConcreteObserver(具体观察者) 维护一个对 ConcreteSubject 的引用; 特定状态与 ConcreteSubject 同步; 实现 Observer 接口,通过 update()方

9、法接收ConcreteSubject 的通知。怎么样,现在总该有点感觉了吧?下面还有一个顺序图,再体会体会呵呵!还没想明白,为什么官方的东西总是看不懂,看来是没当官的命啦!其实观察者模式十分简单,现实生活中的例子更是随处可见,就比如看电视:某个观众就是一个标准的 ConcreteObserver(具体观察者,都符合统一的Observer 接口,即都要通过电视收看节目的观众),电视节目就是 Subject(被观察对象接口,这里体现为无线电视信号)了,不同的频道的节目是不同的 ConcreteSubject(不同频道有不同的节目),观众可以自由决定看电视(registerObserver)或不看电

10、视(removeObserver),而电视节目的变化也会在自动更新(notifyObservers)所有观众的收看内容。怎么样?这回明白了吧!另外观察者模式也叫发布-订阅模式(Publishers + Subscribers = Observer Pattern),跟看电视一样,订阅报纸也是一个很直观的例子,有人发布(Publish = Subject)报纸,有人订阅(Subscribe = Observer)报纸,订阅的人可以定期收到最新发布的报纸,订阅人也可以随时退订。现在大家应该对观察者模式基本都了解了,我们来用这个模式来解决气象站哪个问题。就气象站问题的应用场景来说,WeatherDa

11、ta 可以作为 ConcreteSubject 来看待,而不同的显示设备则可以作为 ConcreteObserver 来看待,也就是说显示设备观察 WeatherData 对象,如果 WeatherData 对象有任何状态变化,则立刻更新显示设备的数据信息。这么说似乎很靠谱了,下面我们再来具体实现一下吧,先从整体结构开始,如下类图:跟前面说的实现方式完全一样,只是这里为所有显示设备又定义了一个统一的接口,这个接口里定义了一个 display()方法,也就是说未来所有实现 Observer 和 DisplayElement 接口的对象应该都可以作为气象监控系统的终端显示设备,不同用户可以在 di

12、splay()方法里任意自定义自己的显示模式。因为为了防止混乱,图 4 中只画了一个具体显示设备对象,即 CurrentConditionsDisplay,跟它同级别的还有 StatisticsDisplay 和 ForcastDisplay,它们在结构上完全相同。下面我们通过具体的代码再进一步理解一下基于观察者模式的气象监控系统的实现。 ISubject:1 using System;23 namespace DesignPatterns.Observer.WeatherData4 5 public interface ISubject6 7 void RegisterObserver(IO

13、bserver o);8 void RemoveObserver(IObserver o);9 void NotifyObserver();10 11 12关于这段代码,似乎没什么好说的了,因为上面已经反复说了很多啦。 IObserver:1 using System;23 namespace DesignPatterns.Observer.WeatherData4 5 public interface IObserver6 7 void Update(float temperature, float humidity, float pressure);8 9 10这里我们给 update()

14、方法定义了三个对应不同气象数据的参数。IDisplayElement:1 using System;23 namespace DesignPatterns.Observer.WeatherData4 5 public interface IDisplayElement6 7 object Display();8 9 10这个类也是超级简单,没什么可解释的。WeatherData:1 using System;2 using System.Collections;34 namespace DesignPatterns.Observer.WeatherData5 6 public class We

15、atherData : ISubject7 8 private ArrayList observers;9 private float temperature;10 private float humidity;11 private float pressure;1213 public WeatherData()14 15 observers = new ArrayList();16 1718 ISubject Members4344 public void MeasurementsChanged()45 46 NotifyObserver();47 4849 public void SetM

16、easurements(float temperature, float humidity,50 float pressure)51 52 this.temperature = temperature;53 this.humidity = humidity;54 this.pressure = pressure;55 MeasurementsChanged();56 57 58 59这个类是 ISubject 的具体实现,内部使用 ArrayList 来记录所有注册的观察者,SetMeasurements() 方法是用来模拟前面提到的在天气状况改变的时候自动触发 MeasurementsCha

17、nged()方法的机制。CurrentConditionsDisplay:1 using System;23 namespace DesignPatterns.Observer.WeatherData4 5 public class CurrentConditionsDisplay : IObserver, IDisplayElement6 7 private float temperature;8 private float humidity;9 private float pressure;10 private ISubject weatherData;1112 public Curren

18、tConditionsDisplay(ISubject weatherData)13 14 this.weatherData = weatherData;15 weatherData.RegisterObserver(this);16 1718 IObserver Members2829 IDisplayElement Members38 39 40这个类是 IObserver 和 IDisplayElement 的具体实现,代表显示当前天气状况的具体显示设备对象,其内部维护了一个 ISubject 类型的变量,该变量在 CurrentConditionsDisplay 的构造函数中被初始化,

19、同时调用ISubject.registerObserver()方法,实现订阅 ISubject。StatisticsDisplay 和 ForcastDisplay:1 using System;2 using System.Text;34 namespace DesignPatterns.Observer.WeatherData5 6 public class StatisticsDisplay : IObserver, IDisplayElement7 8 Members/Members1516 NumberOfReadings Property/NumberOfReadings Prop

20、erty2526 Constructor/Constructor3334 IObserver Members5354 IDisplayElement Members6364 RoundFloatToString/RoundFloatToString7374 75 761 using System;2 using System.Text;34 namespace DesignPatterns.Observer.WeatherData5 6 public class ForcastDisplay : IObserver, IDisplayElement7 8 private float curre

21、ntPressure = 29.92f; 9 private float lastPressure;10 private ISubject weatherData;1112 public ForcastDisplay(ISubject weatherData)13 14 this.weatherData = weatherData;15 weatherData.RegisterObserver(this);16 1718 IObserver Members2728 IDisplayElement Members52 53 54这两个类跟 CurrentConditionsDisplay 基本结

22、构相同,只是 update()和 display()两个方法的具体表现跟CurrentConditionsDisplay 有所不同,具体就不再罗嗦了,看代码便知。上面只是具体的实现代码,并没有具体结果的演示,于是这里提供了一个基于 NUnit 的测试项目,测试的同时也是很好的演示代码,具体不详细说了,大家看代码便知。ObserverWeatherDataDisplayFixture:1 using System;2 using WeatherDataImp = DesignPatterns.Observer.WeatherData;3 using NUnit.Framework;45 name

23、space Test.DesignPatterns.Observer.WeatherData6 7 TestFixture8 public class ObserverWeatherDataDisplayFixture9 10 Members/Members1617 TestFixtureSetUp Init()/ TestFixtureSetUp Init()2728 TestFixtureTearDown Dispose()/TestFixtureTearDown Dispose()3839 TestCurrentConditionsDisplay/TestCurrentCondition

24、sDisplay4950 TestForecastDisplay/TestForecastDisplay6869 TestStatisticsDisplay/TestStatisticsDisplay88 89 90完整代码下载: (VS2005、NUnit2.2/2.4)应用场景和优缺点上面已经对观察者模式做了比较详细的介绍,还是那句话,人无完人,模式也不是万能的,我们要用好设计模式来解决我们的实际问题,就必须熟知模式的应用场景和优缺点:观察者模式的应用场景:1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。观察者模式的优点:1、 Subject 和 Observer 之间是松偶合的,分别可以各自独立改变。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育教学资料库 > 精品笔记

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。