转载自:http://blog.csdn.net/jsagacity/article/details/72566665
观察者模式(observer pattern):在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
观察者模式的结构
一个软件系统包含很多对象,就好比一个生态系统包含很多生物一样,生态系统里面的各种生物都彼此依赖和约束着,只要哪个生物的状态发生变化,其他的都会随之改变。
同样,一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。
下面就是观察者模式的最简单的框架:
观察者模式所涉及的角色有:
目标对象(Subject):目标对象把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。目标对象提供一个接口,可以增加和删除观察者对象,目标对象又叫做抽象被观察者(Observable)角色。
具体的目标对象(ConcreteSubject):将有关状态存入具体观察者对象;在具体目标的内部状态改变时,给所有登记过的观察者发出通知。具体目标对象又叫做具体被观察者(Concrete Observable)角色。
观察者接口(Observer):为所有的具体观察者定义一个接口,在得到目标的通知时更新自己,这个接口叫做更新接口。
具体的观察者对象(ConcreteObserver):存储与目标的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与目标的状态相协调。如果需要,具体观察者角色可以保持一个指向具体目标对象的引用。
目标对象类(Subject):
具体的目标对象类(ConcreteSubject):
观察者接口(Observer):
具体的观察者对象(ConcreteObserver):
以上是观察者模式的框架源码,以后只要是适合观察者的使用,就可以直接拿这个框架来修改使用。
接下来我们将结合一个非常形象的例子,来使用上面的观察者模式的源码框架。
小星是在气象台工作的,这段时间他正在追求一个女孩子,为了体现关心,小星把时时观察到的天气情况都告诉这个女孩子,并组织约会的时间和地点。要想更快的追求这个女孩子,那么久必须“收买”她妈,这个女孩子的妈妈喜欢逛街购物,逛街购物也需要好天气,这是小星也可以把天晴情况一并的更新给这个未来岳母。现在场景中的目标对象就是天气的情况,对天气的观察者就是小星要追的这个女孩子,以及她的老妈。
好接下来我们用代码实现一下:
天气的目标对象(WeatherSubject):
具体的天气目标对象(ConcreteWeatherSubject):
观察者接口(Observer):
具体的观察者对象(ConcreteObserver):
接下来客户端类实现一下(Client):
运行结果:
观察者模式又分为两种模型:推模型和拉模型
推模型:目标对象主动向观察者推送目标的详细信息,推送的信息通常是目标对象的全部信息或部分信息。
拉模型:目标对象在通知观察者的时候,只传递少量的信息。如果观察者需要更新具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者。
根据以上的描述,你会发现上面的例子是典型的拉模型,下面我们把它改成推模型(红框就是改动的地方):
天气的目标对象(WeatherSubject):
具体的天气目标对象(ConcreteWeatherSubject):
观察者接口(Observer):
具体的观察者对象(ConcreteObserver):
客户端类(Client)不用改动,接下来运行一下,查看运行结果和上面的推模型一样的:
两种模型做比较:
推模型:
1.推模型是假定目标对象知道观察者需要的数据
2.推模型会使观察者对象难以复用
拉模型:
1.拉模型是目标对象不知道观察者具体需要什么数据,因此把自身传给观察者,由观察者来取值。
2.拉模型下,update方法的参数是目标对象本身,基本上可以适应各种情况的需要
其实Java中也提供了对观察者模式的实现
在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。
上面的例子我们用Java提供的支持来实现一下:
天气目标的具体实现类(ConcreteWeatherSubject):
具体的观察者对象(ConcreteObserver):
客户端实现类(Client):
运行结果(推模型):
运行结果(拉模型):
Java实现与自己实现的对比:
1、不需要再定义观察者和目标的接口了,JDK帮忙定义了;
2、具体的目标实现里面不需要再维护观察者的注册信息了,这个在Java中的Observable类里面已经帮忙实现好了;
3、触发通知的方式有一点变化,需要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能;
4、具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个是Java在定义的时候,就已经考虑进去的。
观察者模式的优点:
1、观察者模式实现了观察者和目标之间的抽象耦合;
2、观察者模式实现了动态联动;
3、观察者模式支持广播通信。
缺点:可能会引起无谓的操作。
观察者适用场景:
1、当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化;
2、如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变;
3、当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的。