观察者模式(Observer):定义了对象之间的一对多关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
实现观察者模式的方法有多种,但是以包含Subject与Observer接口的类设计的做法最常见,下面看看观察者模式的类图:
主题(Subject)是真正拥有数据的人,观察者则是主题的依赖者,在主题数据变化时接收通知并更新。这样比起让许多对象控制同一份数据来,可以得到更干净的OO设计。
主题与观察者之间是松耦合的,它们可以交互,但不知道彼此的细节。比如对于观察者,主题只知道观察者实现了某个接口,主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表;同样的,我们也可以在任何时候删除某些观察者。因为松耦合的关系,改变主题或观察者其中一方,并不会影响另一方。松耦合的设计能让我们建立有弹性的OO系统以应对变化,因为对象之间的互相依赖降到了最低。
下面就以经典的气象监测问题为例,说说观察者模式的应用:
此气象站系统只包括三个部分:气象站、WeatherData对象、布告板。
工作的流程:WeatherData对象从气象站获取最新的测量数据(温度、湿度、气压),并及时更新到三个布告板(显示装置)上。
假设从气象站获取数据的方法已经实现好了,那么我们只需要考虑如何将新的数据更新到三个布告板上,而且要尽量实现系统可拓展,让其他开发人员可以定制布告板,用户可以任意的添加或删除布告板,那么我们如何建立这个系统呢?
当然是使用观察者模式,这里的WeatherData类正是观察者模式中的“一”,即主题;而布告板就是“多”,即观察者;这样就建立起了一对多的依赖关系。WeatherData对象是真正拥有数据的一方,包括温度、湿度、气压,当这些值改变时,必须通知所有的布告板,好让它们各自做出处理。在这里,布告板作为Observer为了获取数据,必须先向WeatherData对象注册,一旦WeatherData知道有某个布告板的存在,就会适时地调用布告板的某个公共的接口(例如. update)来告诉布告板观测值是多少。由于update()方法是所有布告板公共的接口,所以需要在布告板的基类(Java中说接口)中定义。下面是设计图:
- WeatherData实现主题(Subject)接口
- 布告板实现观察者(Observer)接口,这样主题在需要通知观察者时,就有了一个共同的接口
- 同时还为布告板建立一个共同的接口DisplayElement,用于实现display()方法
- 每个布告板中应该声明一个Subject接口类对象
C++实现:
1 | // Subject接口类 |
1 | // Observer接口类 |
1 | // 接口类 用于display |
1 | // WeatherData实现Subject接口 |
1 | // “目前状况”布告板 实现观察者接口 |
1 | // 数据统计布告板 |
1 | // 天气预报布告板 |
1 | // 程序入口WeatherStation.cpp |