今天来根据书上的例子讲讲自己对观察者模式这个重要的设计模式的理解:
越往后面学,我们会接触到更多这样的“行话”,就是懂设计模式的人,你只要跟他一提起某个涉及模式,并不需要别的交流,你们两个之间就能相互明白对方是怎么想的,这些“行话”,就是我们所说的专用名词汇表,就像你去快餐店点餐,点一个巨无霸,那工作人员就会给你一个巨无霸,而不需要你说:我要两个面包皮,中间一些生菜,一个肉片......
看这篇文章之前,你可能像我一样,初次接触设计模式,并不知道业界的各种设计模式,但是,希望离开时,我可以把观察者模式的概念渗透给你!!!
每个模式的命名都是颇有讲究而不失活泼的,就像今天的观察者模式,你可以首先在脑子里构想一幅场景:有一个与世隔绝的镇子,镇子上只有一家报刊亭,是联系镇子与外界的唯一通道,镇子上的居民通过到报刊亭购买报纸得到新闻,我们把拥有资源的报刊亭称作“主题”对象,而将来报刊亭买报纸的居民称为“观察者”,居民们买到报纸后可以对报纸有自己的利用方式(PS:有些居民将报纸上的新闻讲给自己的朋友,有些居民买报纸是为了自己阅读),就是说,我们的“观察者”获得了资源后是要使用资源做自己的事情的。当居民想要了解新闻的时候,他选择订购报纸,当居民不再想了解当前发生的新闻的时候他也可以随时决定自己不再购买报纸。
我们怎么用程序来描述这种有资源的“主题”对象向“观察者”提供资源,“观察者”向“主题”对象注册(类似与订报纸)而获取不断更新的资源,并且可以在不再需要更新的资源的时候在“主题”的对象表中注销自己的关系呢???
考虑另一种“观察者模式”的例子:气象站向天气APP提供不断更新的天气信息(温度,湿度,气压等),天气APP在得到气象站的不断更新的资源后,可以向下载该APP的用户提供一些他们需要的天气统计信息:如,当前天气信息模块向用户通知当前更新的最新天气情况,最近几天天气平均情况模板保留近几次的天气信息,求得平均值后显示给用户做判断,还有天气预报模块,根据当前天气状况向用户提供未来天气状况的参考。(这个例子是一个很明显的观察者模式,读者可以自己找找看其中的主题对象是谁,观察者又是谁)
那么问题来了?我们怎么用程序设计实现呢?
最容易想到的便是,先建立一个主题类,有自己的许多资源做成员变量,比如private pressure,private humidity(湿度),private tempertuer。然后主题对象提供一组方法,当从远方的监测站向气象站提供最新的气象信息时,能通知各个观察者更新自己的数据,怎么让不统一的观察者都能被主题对象更新呢?比如当前天气信息模块,最近平均天气模块和天气预报模块,我们只需要在主题对象中调用 : 当前天气信息模块.update(温度,湿度,气压);最近平均天气模块.update(温度,湿度,气压);天气预报模块.update(温度,湿度,气压);
不难发现,不同的观察者都需要一个update(温度,湿度,气压)来更新自己的资源,因此,可以设计一个接口,接口中将update()方法作为抽象方法,在不同的模块中给出具体的实现方法。于是,第一种设计模式可以表示为:
主题对象实现类 其中一个观察者的实现类
当我们需要增加一个新的模块(增加一个新的观察者)的时候,我们要怎么做呢?我们要打开主题对象实现类,向其中插入一条 xxx模块.update(温度,湿度,气压),这样一个方法,这很明显有悖我们的初衷,而且不是一种易于扩展的实现方式。我们怎么做能够再增加观察者的时候可以不改变原有的旧的实现类的方法而做到满足新的观察者的要求呢?也就是说要彻底改变主题对象中对它的观察者一个一个挨个调用他们update()方法完成通知观察者更新消息这种原有的不利于扩展的做法,也就是说怎么样可以一次性操作该主题所拥有的所有观察者呢?这是一个一对多的关系,主题是一,而且它拥有资源,而观察者是多,他需要从主题中获取资源。我们不妨在主题对象中设置一个观察者的动态数组,有新的观察者就把观察者放入它的动态数组之中,当有的观察者要离开时就简单的在观察者数组中删除这个观察者,而更新时只需要遍历整个数组调用每个观察者都实现的接口中的update(温度,湿度,气压)方法,就能完成通知观察者更新资源信息的过程,而且可以屏蔽观察者的加入和离去。那进一步思考,观察者怎么向主题对象传递自己要加入到主题的观察者对象数组或者离开该数组不再接受新的更新通知的请求呢?不妨在主题对象中提供注册和撤销方法,操作主题对象的观察者数组,动态管理当前的观察者数量。在观察者一方也应拥有一个主题对象的引用,这样可以调用主题对象提供的注册,注销,更新方法等将自己传入方法做参数,实现对主题对象的观察者数组的更改:
这种设计可以体现为:
具体编程实现为:
需要注意的几点:
1.主题对象拥有资源做成员变量,并且拥有一个观察者对象的动态数组。并且实现注册,注销,更新(数组循环更新)的方法(注册和注销方法将一个观察者对象做参数传入)。
2.主题对象所实现的接口中应提供注册,注销和更新的抽象方法。
3.观察者对象所实现的接口中应该包含更新update()方法。
4.具体的观察者实现类中应有一个主题对象做成员变量,在观察者的构造函数中应完成主题对象的创建和向主题对象注册的实现。同时每个具体的观察者重写接口中的update(方法),并提供自己独特的展示方式(重写Displayable接口中的display())方法。
5.在编写测试类时,应首先创建一个主题对象,再创建不同种类的观察者对象,将前面的主题对象传入观察者对象的构造函数做参数并在构造时完成观察者对象所拥有的主题对象的初始化和向该主题对象完成注册过程。最后只需要在主题对象中调用setMeasurements(温度,湿度,气压)方法来模拟新数据到达,并在setMeasurements(温度,湿度,气压)方法的末尾调用measurementChanged()方法,在measurementChanged()方法中调用notifyObservers()方法,最后在notifyObservers()方法中通过数组循环调用每个其中的观察者对象的update()方法,即可完成对贯彻着信息的新数据到达的自动更新的过程。
主题对象实现 观察者对象实现类
使用到的三个接口的实现 测试类的实现
今天的观察者模式的设计实现教会了我们一些在设计时的重要原则:
1.为了交互对象之间的松耦合设计而努力。
2.开闭原则(对拓展开放,对修改关闭)。
最后给出观察者模式的定义:
观察者模式定义了对象之间的一对多依赖,这样一来,当对象改变状态时,它的所有依赖者都会收到通知并自动更新。
最后的最后,给出一个观察者模式最直观的示意图,满足这种图像关系的应用场景可以使用观察者模式来实现!!!!同时,读到这里时希望读者能在心里默念三遍(一对多关系!一对多关系!!一对多关系!!!):
观察者模式示意图!!!一对多哦!!!