天气预报中的观察者模式
1.观察者模式的介绍
观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
2.观察者模式的结构(以天气预报系统为例)
2.1.被观察者
主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法。
2.2.观察者
观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
2.3.具体被观察者
具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
2.4.具体观察者
具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。
具体代码如下:
被观察者 为天气状态
1 /** 2 * 3 * Weather can be observed by implementing {@link WeatherObserver} interface and registering as 4 * listener. 5 * 6 */ 7 public class Weather { 8 9 private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class); 10 11 private WeatherType currentWeather; 12 private List<WeatherObserver> observers; 13 14 public Weather() { 15 observers = new ArrayList<>(); 16 currentWeather = WeatherType.SUNNY; 17 } 18 19 public void addObserver(WeatherObserver obs) { 20 observers.add(obs); 21 } 22 23 public void removeObserver(WeatherObserver obs) { 24 observers.remove(obs); 25 } 26 27 /** 28 * Makes time pass for weather 29 */ 30 public void timePasses() { 31 WeatherType[] enumValues = WeatherType.values(); 32 currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; 33 LOGGER.info("The weather changed to {}.", currentWeather); 34 notifyObservers(); 35 } 36 37 private void notifyObservers() { 38 for (WeatherObserver obs : observers) { 39 obs.update(currentWeather); 40 } 41 } 42 }
观察者为抽象的天气状态观察者
1 package com.iluwatar.observer; 2 3 /** 4 * 5 * Observer interface. 6 * 7 */ 8 public interface WeatherObserver { 9 10 void update(WeatherType currentWeather); 11 12 }
具体被观察者 为具体的天气状态 (该项目中 由于被观察者只有唯一一个,因此被观察者和具体被观察者为同一个)
1 public class Weather { 2 3 private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class); 4 5 private WeatherType currentWeather; 6 private List<WeatherObserver> observers; 7 8 public Weather() { 9 observers = new ArrayList<>(); 10 currentWeather = WeatherType.SUNNY; 11 } 12 13 public void addObserver(WeatherObserver obs) { 14 observers.add(obs); 15 } 16 17 public void removeObserver(WeatherObserver obs) { 18 observers.remove(obs); 19 } 20 21 /** 22 * Makes time pass for weather 23 */ 24 public void timePasses() { 25 WeatherType[] enumValues = WeatherType.values(); 26 currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; 27 LOGGER.info("The weather changed to {}.", currentWeather); 28 notifyObservers(); 29 } 30 31 private void notifyObservers() { 32 for (WeatherObserver obs : observers) { 33 obs.update(currentWeather); 34 } 35 } 36 }
具体观察者为天气预报与人物的活动 具体观察者根据被观察者(天气状态)的更新而实时更新天气预报和人物的活动状态
1 package com.iluwatar.observer; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 6 /** 7 * 8 * Orcs 9 * 10 */ 11 public class Orcs implements WeatherObserver { 12 13 private static final Logger LOGGER = LoggerFactory.getLogger(Orcs.class); 14 15 @Override 16 public void update(WeatherType currentWeather) { 17 switch (currentWeather) { 18 case COLD: 19 LOGGER.info("The orcs are freezing cold."); 20 break; 21 case RAINY: 22 LOGGER.info("The orcs are dripping wet."); 23 break; 24 case SUNNY: 25 LOGGER.info("The sun hurts the orcs' eyes."); 26 break; 27 case WINDY: 28 LOGGER.info("The orc smell almost vanishes in the wind."); 29 break; 30 default: 31 break; 32 } 33 } 34 }
1 package com.iluwatar.observer; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 6 /** 7 * 8 * Hobbits 9 * 10 */ 11 public class Hobbits implements WeatherObserver { 12 13 private static final Logger LOGGER = LoggerFactory.getLogger(Hobbits.class); 14 15 @Override 16 public void update(WeatherType currentWeather) { 17 switch (currentWeather) { 18 case COLD: 19 LOGGER.info("The hobbits are shivering in the cold weather."); 20 break; 21 case RAINY: 22 LOGGER.info("The hobbits look for cover from the rain."); 23 break; 24 case SUNNY: 25 LOGGER.info("The happy hobbits bade in the warm sun."); 26 break; 27 case WINDY: 28 LOGGER.info("The hobbits hold their hats tightly in the windy weather."); 29 break; 30 default: 31 break; 32 } 33 } 34 }
APP调用
1 public class App { 2 3 private static final Logger LOGGER = LoggerFactory.getLogger(App.class); 4 5 /** 6 * Program entry point 7 * 8 * @param args command line args 9 */ 10 public static void main(String[] args) { 11 12 Weather weather = new Weather(); 13 weather.addObserver(new Orcs()); 14 weather.addObserver(new Hobbits()); 15 16 weather.timePasses(); 17 weather.timePasses(); 18 weather.timePasses(); 19 weather.timePasses(); 20 21 // Generic observer inspired by Java Generics and Collection by Naftalin & Wadler 22 LOGGER.info("--Running generic version--"); 23 GWeather gWeather = new GWeather(); 24 gWeather.addObserver(new GOrcs()); 25 gWeather.addObserver(new GHobbits()); 26 27 gWeather.timePasses(); 28 gWeather.timePasses(); 29 gWeather.timePasses(); 30 gWeather.timePasses(); 31 } 32 }
3.实现原理
4.观察者模式优缺点
优点:
观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。观察者模式定义了稳定的消息更新传递机制,可以实现一对多的广播通信,同时在被观察者和观察者之间建立的关系并不是很紧密,它们是不同的抽象,符合“低耦合”的系统设计理念。
缺点:
观察者模式建立的消息传递机制是低效的,如果建立了多个观察者,可能会影响消息传递的时间,这只会让观察者知道了被观察者发生了变化的结果,却不知道变化发生的过程。另外如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间,并且如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用。