初学设计模式【2】观察者模式——Observer
形象描述
什么是观察者模式呢?个人感觉,这个和现在很流行的微信比较像。在微信中我们可以Follow一些公众号,比如明人、明星、企业等。这样呢,我们就会即时收到他们推送给我们的消息。这里面有两个角色,一个就是我们所Follow的这些公众号,还有就是我们自己。在观察者模式中我们称我们Follow的对象为“主题”或“可被观察者”,我们自己就是“观察者”。
定义
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
UML类图
说明:
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。为什么呢?对于观察者而言,主题只知道观察者们实现了一个Observer接口,其它的任何细节主题不需要知道。正因为这样,我们可以随时增加或删除新的观察者,主题所依赖的只是一个实现了Observer接口的对象的列表,一般我们是通过在主题类中维护一个ArrayList来保存对所有的观察者的引用。并且,我们对观察者的这些操作不会导致主题中任何代码的改动,因为当有新的观察者或要删除某观察者时我们只需要调用主题类暴露的注册或移除方法即可。除此之外,我们还可以独立的复用主题或观察者类。
只要他们之间的接口规范被遵守,我们就可以自由地改变它们。这里可以引出一个设计原则:为了交互对象之间的松耦合设计而努力。
实例
有一个气象站,以及三个公告牌。气象站可以提供三种气象数据:当前天气状况(晴、阴……),平均温度,当前气压。我们需要完成这样的功能:当气象站提供的数据发生变化时,我们要即时将它们更新到三个公告牌上。很显示这里气象站是“主题”,公告牌是观察者。请看UML类图。
UML类图
分析:这里面有两个地方我觉得值得注意。1.Subject持有对Observer的引用 2.各具体观察者类持有对Subject的引用。当初对这两点理解的并不是太清楚。我们可以想一下我们微信的例子:Subject就相当于我们关注的某个微信公众号(比如就是‘开复老师’的吧)。开复老师的粉丝就是我们了,当然有好多好多了,对应上图中的Notice1,Notice2...
首先,分析为什么Subject要持有对Observer的引用。想一想要是开复老师不知道都是哪些人Follow了他,他怎么知道给谁推送。再看一下为什么观察者类要持有对Subject的引用。想一下,假如我们不知道开复老师的微信号我们怎么能Follow他呢。这里也一样,只有我们持有了对Subject的引用我们才能在它上面注册,然后它才能给我们发消息。
代码:
1.Subject接口
1 public interface Subject { 2 public void registerObserver(Observer o); 3 4 public void removeObserver(Observer o); 5 6 public void notifyObserver(); 7 8 }
2.Observer接口
1 public interface Observer { 2 public void update(WeatherInfo info); 3 public void register(WeatherData weatherData); 4 }
3.DisplayElement接口
1 public interface DisplayElement { 2 public void Display(); 3 }
4.WeatherData
1 import java.util.ArrayList; 2 3 public class WeatherData implements Subject { 4 private WeatherInfo info; 5 private ArrayList<Observer> Observers; 6 7 public WeatherData() { 8 Observers = new ArrayList<Observer>(); 9 info = new WeatherInfo(); 10 } 11 12 @Override 13 public void registerObserver(Observer o) { 14 Observers.add(o); 15 } 16 17 @Override 18 public void removeObserver(Observer o) { 19 int index = Observers.indexOf(o); 20 if (index != -1) 21 Observers.remove(index); 22 } 23 24 @Override 25 public void notifyObserver() { 26 for (Observer o : Observers) 27 o.update(info); 28 } 29 30 // 模拟获取天气数据 31 public void setMeasureMents(String state, float temperature, float pressure) { 32 info.setWeatherState(state); 33 info.setTemperature(temperature); 34 info.setPressure(pressure); 35 notifyObserver(); 36 } 37 38 }
5.观察者类
1 //show weather state,such as clear,cloudy... 2 public class Notice1 implements Observer, DisplayElement { 3 private WeatherInfo info; 4 5 @Override 6 public void register(WeatherData weatherData) { 7 weatherData.registerObserver(this); 8 } 9 10 @Override 11 public void Display() { 12 System.out.println("State:" + info.getWeatherState()); 13 } 14 15 @Override 16 public void update(WeatherInfo info) { 17 this.info = info; 18 Display(); 19 } 20 21 }
注:Notice2,Notice3与此类似,不再贴了。
6.测试类
1 import org.junit.Test; 2 3 public class TestObserver { 4 @Test 5 public void test() { 6 WeatherData data = new WeatherData(); 7 Notice1 notice1 = new Notice1(); 8 notice1.register(data); 9 Notice2 notice2 = new Notice2(); 10 notice2.register(data); 11 Notice3 notice3 = new Notice3(); 12 notice3.register(data); 13 //模拟天气变化 14 data.setMeasureMents("clear", 31f, 77f); 15 } 16 }
输出:
PS:以上内容是我在阅读《Head First Design Pattern》过程中加上自己的理解,整理改编过来的。有不妥之处欢迎批评指正,谢谢!貌似Java提供的有专门的API关于观察者模式。我会在下一篇中总结一下。