场景
我们接到一个来自气象局的需求:气象局需要我们构建一套系统,这系统有两个公告牌,分别用于显示当前的实时天气和未来几天的天气预报。当气象局发布新的天气数据(WeatherData)后,两个公告牌上显示的天气数据必须实时更新。气象局同时要求我们保证程序拥有足够的可扩展性,因为后期随时可能要新增新的公告牌。
概况
这套系统中主要包括三个部分:气象站(获取天气数据的物理设备)、WeatherData(追踪来自气象站的数据,并更新公告牌)、公告牌(用于展示天气数据)
WeatherData知道如何跟气象站联系,以获得天气数据。当天气数据有更新时,WeatherData会更新两个公告牌用于展示新的天气数据。
错误示范
我们现来看看隔壁老王的实现思路:
上面这段代码是典型的针对实现编程,这会导致我们以后增加或删除公告牌时必须修改程序。我们现在来看看观察者模式,然后再回来看看如何将观察者模式应用到这个程序。
观察者模式介绍
观察者模式面向的需求是:A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者、小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会错过任何瞬间。程序里的观察者和这种真正的【观察】略有不同,观察者不需要时刻盯着被观察者(例如A不需要每隔1ms就检查一次B的状态),二是采用注册(Register)或者成为订阅(Subscribe)的方式告诉被观察者:我需要你的某某状态,你要在它变化时通知我。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。
观察者模式通常基于Subject和Observer接口类来设计,下面是是类图:
观察者模式的应用
结合上面的类图,我们现在将观察者模式应用到WeatherData项目中来。于是有了下面这张类图:
主题接口
观察者接口
公告牌用于显示的公共接口
下面我们再来看看WeatherData是如何实现的
显示当前天气的公告牌CurrentConditionsDisplay
显示未来几天天气的公告牌ForecastDisplay
到这里,我们整个气象局的WeatherData应用就改造完成了。两个公告牌和实现了和接口,在他们的构造方法中会调用的方法将自己注册成观察者,这样被观察者就会持有观察者的应用,并将它们保存到一个集合中。当被观察者状态发送变化时就会遍历这个集合,循环调用观察者更新数据的方法。后面如果我们需要增加或者删除公告牌就只需要新增或者删除实现了和接口的公告牌就好了。
观察者模式将观察者和主题(被观察者)彻底解耦,主题只知道观察者实现了某一接口(也就是Observer接口)。并不需要观察者的具体类是谁、做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现了接口的对象列表。
好了,我们测试下利用观察者模式重构后的程序:
输出结果:
源码地址:https://github.com/BaronZ88/DesignPatterns/tree/master/src/com/baron/patterns/observer
知乎专栏:https://zhuanlan.zhihu.com/baron
GitHub:https://github.com/BaronZ88
领取专属 10元无门槛券
私享最新 技术干货