OBSERVER(观察者)-对象行为型模式•1.意图•定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。•2.别名•依赖(Dependents),发布-订阅(Publis-Subscribe)•3.动机当前环境中有如图所示的三个不同的显示项。用户也可以获取天气状态和预报。•我们需要创建一个应用程序,可以根据天气数据对象来更新当前状况、气象统计以及天气预报的三个布告板。这三个方法分别返回最近的天气测量值,包括气温、湿度和气压值。我们不必关心这些变量是怎么设置的,对象WeatherData知道如何从气象站获取更新的信息。•我们的工作是,实现measurementChanged()方法,使之可以更新当前状况、气象统计以及天气预报的三个值。•WeatherData类中有三个获取测量值的方法,分别用以获取温度、湿度以及气压;–getTemperature()–getHumidity()–getPressure()•当有新的测量值生效时可以调用measurementsChanged()方法。(我们不必知道或者关心这个方法是如何被调用的,只需要知道有这个方法即可。)•我们需要实现三个天气数据的显示功能:平均温度:22°最低温度:16°最高温度:28°目前状况温度:25°湿度:60气压:天气预报•系统必须具备扩展性----其他开发人员可以创建新的自定义的显示,用户根据需要可以增加或者删除显示功能。?•第一次实现……:•在Weather-O-Rama的开发人员的指导下在measurementsChanged()方法中加入了我们自己的代码。通过调用getTemperature(),getHumidity(),getPressure()(这三个方法已经被实现了)获取最近的天气数据。更新这些显示的功能:调用每一个显示的功能来更新其显示项(最新的天气数据)•我们的实现有什么问题吗?•A.面向实现在编程,而不是面向接口;•B.任意一个新的显示功能要求,我们必须修改代码;•C.我们无法在运行时增加(或者删除)显示的功能;•D.显示功能没有通用的接口;•E.对于容易变化的部分我们没有实现封装;•F.对于WeatherData类来说,我们违背了类的开-闭原则(我们在WeatherData类里加入了新的代码,使得WeatherData类发生了改变)。在此处我们是用代码具体实现了Display功能,将来我们如果想增加或者删除Display功能将变得很困难,除非我们又来修改这些代码。从分析来看,这些地方是属于容易变化的部分,我们应该把这些部分封装起来,使得由于这些变动而不会改变原有的代码。•这里的实现看起来我们似乎已经使用了一个通用的接口来告诉Display功能⋯它们都有一个update()方法来获取temp,humidity,和Pressure的值。•很显然,系统的维护性很不好。•观察者模式的构成:•Publishers(发行者)+Subscribers(订阅者)•=ObserverPattern(观察者模式)一对多当主题内的数据改变,就会通知观察者•依赖的对象(Observer依赖于Subject,当Subject的数据或者状态发声改变时,Observer可以自动得到通知和更新。)•Observer模式描述了如何建立这种关系。这一描述中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。作为对这个通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。•这种交互也称为发布-订阅。目标是通知的发布者。它发出通知时并不需要知道谁是它的观察者。可以有任意数目的观察者订阅并接收通知。•4.适用性•在以下任一情况下可以使用观察者描述:–当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立的改变和复用。–当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。–当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的。•5.结构•6.参与者•Subject(目标):目标知道它的观察者。可以有任意多的观察者观察同一个目标;提供注册和删除观察者对象的接口。•Observer(观察者):为那些在目标发生改变时需获得通知的对象定义一个更新接口。•ConcreteSubject(具体目标):将有关状态存入各自ConcreteObserver对象;当它的状态发生改变时,向它的各个观察者发出通知。•ConcreteObserver(具体观察者):维护一个指向ConcreteSubject对象的引用;存储有关状态,这些状态应与目标的状态保持一致;实现Observer的更新接口以使自身状态与目标的状态保持一致。模式代码原型staticvoidMain(){//ConfigureObserverpatternConcreteSubjects=newConcreteSubject();s.Attach(newConcreteObserver(s,X));s.Attach(newConcreteObserver(s,Y));s.Attach(newConcreteObserver(s,Z));//Changesubjectandnotifyobserverss.SubjectState=ABC;s.Notify();//WaitforuserConsole.ReadKey();}OutputObserverX'snewstateisABCObserverY'snewstateisABCObserverZ'snewstateisABCabstractclassSubject{privateListObserver_observers=newListObserver();publicvoidAttach(Observerobserver){_observers.Add(observer);}publicvoidDetach(Observerobserver){_observers.Remove(observer);}publicvoidNotify(){foreach(Observeroin_observers){o.Update();}}}classConcreteSubject:Subject{privatestring_subjectState;//GetsorsetssubjectstatepublicstringSubjectState{get{return_subjectState;}set{_subjectState=value;}}}abstractclassObserver{publicabstractvoidUpdate();}classConcreteObserver:Observer{privatestring_name;privatestring_observerState;privateConcreteSubject_subject;//ConstructorpublicConcreteObserver(ConcreteSubjectsubject,stringname){this._subject=subject;this._name=name;}publicoverridevoidUpdate(){_observerState=_subject.SubjectState;Console.WriteLine(Observer{0}'snewstateis{1},_name,observerState);}//GetsorsetssubjectpublicConcreteSubjectSubject{get{return_subject;}set{_subject=value;}}}•7.协作•当ConcreteSubject发生任何可能导致其观察者与其自身状态不一致的改变时,它将通知它的各个观察者。•在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息。ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致。•8.效果•Observer描述允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者,反之亦然。它也使你可以在不该点目标和其他的观察者的前提下增加观察者。•下面是观察者描述其他一些优缺点:•1.目标和观察者间的抽象耦合。•2.支持广播通信•3.意外的更新•9.实现•1.创建目标到其观察者之间的映射。•2.观察多个目标。•3.谁触发更新•4.对已删除目标的悬挂引用•5.在发出通知前确保目标的状态自身是一致的•6.避免特定于观察者的更新协议-推/拉模型•7.显式的指定感兴趣的改变•8.封装复杂的更新语义•9.结合目标类和观察者类•10.代码示例见文档。•11.使用Java内置的观察者模式:•java.util包内包含最基本的Observer接口与Observable类;•这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。•有了Java内置的支持,你只需要扩展(继承)Observable,并告诉它何时该通知观察者,一切就完成了,剩下的事API会帮你做。•Java内置的观察者模式如何运作:•将对象变成观察者:–实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者时,调用deleteObserver().•可观察者送出通知:–利用扩展java.util.Observable接口产生“可观察者类”–先调用setChanged()方法,标记状态已经改变的事实;–调用两种notifyObservers()方法中的一个•notifyObservers()•notifyObervers(Objectarg)•观察者接收通知:–update(Observableo,Objectarg)–主题本身当作第一个变量,好让观察者知道是哪个主题通知它的;–数据对象作为第二个变量•setChanged(){•changed=true•}•notifyObservers(Objectarg){•if(changed){•foreveryobserveronthelist{•callupdate(this,arg)•}•changed=false•}•}•notifyObservers(){•notifyObservers(null)•}•Java.util.Observable的黑暗面:–Observable是一个类,你必须设计一个类继承它。可能会引起“多重继承”的问题。–Observable将关键的方法保护起来,setChanged()方法被保护起来了(被定义成protected)。这意味着:除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象中来。这个设计违反了第二个设计原则:“多用组合,少用继承”。