设计模式之观察者模式
背景
以气象监测为案例来学习观察者模式,整个系统分为三个部分:气象监测站(获取气象数据)、WeatherData对象(将获取到的气象数据更新到公告板)和公告板(用来显示气象的数据,实时更新)。
所以我们的任务就是利用WeatherData对象获取天气信息然后实时的更新的到公告板上,现在我们可以获取到的信息有:
- 我们已经可以获取的信息是WeatherData中有获取数据的getter方法
- 进行数据显示的公告板是不止一个的,可以根据数据显示不同的信息。比如我们需要实现三个布告板的功能:“目前状况布告板(即上图中的布告板)”、“气象统计布告板(统计气温的数据)”、“天气预报布告板(预报今天的天气状况)”。
- 这些显示数据的公告板必须可扩展,以方便之后根据不同的数据在进行显示不同的公告板。
看看以下这种实现:
//这个是在WeatherData中的测量值变化之后的调用方法
public void messurementChanged() {
//通过WeatherData提供的方法获取相关数据
float temp = getTemperature();
float humi = getHumidity();
float press = getPressure();
//然后调用相应的布告板进行信息的显示
currentConditionDis.update(temp, humi, press);
staticDis.update(temp, humi, press);
forecastDis.update(temp, humi, press);
}
但是这种实现有几个问题:
- 在更新布告板的时候是根据具体实现进行的,不同的布告板有不同的实现,直接对不同的实现调用update方法, 违反了面向接口编程的设计原则,这会导致之后增加或者修改布告板的时候还得修改WeatherData。
- 布告板可能有多个,对于新增一个布告板还需要修改WeatherData中的代码,显然没有达到封装的效果,我们应该将那些不定的、会改变的地方进行封装起来。
- 对于更新方法update来说,起码是可以抽象出来作为一个共同的方法。
观察者模式
即发布订阅模式:发布者+订阅者 = 观察者模式(下文中的发布订阅模式即指观察者模式)
借用报社的流程来理解发布订阅模式
报社(发布者):
- 每天都会根据当前的新闻发布报纸信息。但是,报社并不会把报纸随便发给每个人,在报社的系统中,存储了一个用户表,里面记录了所有订阅了报社报纸的用户信息。
- 但是一个报社也不会只出版一种报纸,他们会出版很多种类的报纸,那么,报社又如何知道将哪一个种类的报纸送给用户了。所以,在用户表中还记录了某个用户订阅了哪一种,即哪个主题(Subject)的报纸,根据这些信息,报社就可以准确的将某个主题的报纸送到订阅了该主题的用户手上。
读者(订阅者):
- 首先,读者不会什么报纸都想看,也不会去定所有的报社的报纸,所以,读者会根据自己的需要,选择一个报社(发布者)和该报社的某一款报纸(Subject)。
- 然后读者给报社下单,并告诉报社往哪儿送这个报纸。
- 如果用户在某个报社订阅了某个报纸之后,又不想要这份报纸了,读者还可以告诉这个报社,自己不想再订阅 某个主题的报纸了,于是,报社就会将该用户从订阅了该报纸的用户列表中删除,以后这个报纸又更新了就不会再送给这个读者了。
观察者模式定义
观察者模式定义了对象之间的一对多的依赖关系,主题是‘一’,它依赖了‘多’个订阅者,每当主题发生改变就会通知所有的依赖者并进行更新。
根据上面的讨论,可以得到如下几点:
- 在主题中应该包含了一个集合数组用来记录所有的订阅给主题的观察者信息
- 在观察者中应该包含了一个主题信息,用