-
观察者模式定义了对象之间的一对多的依赖关系,当一个对象改变状态时,它的所有依赖着都会受到通知并且自动更新。
-
观察者模式的UML图
-
Subject:抽象主题,把所有对观察者的引用保存在一个聚集里,每一个主题可以有多个观察者。Observer:抽象观察者,为所有具体观察者定义了一个接口,在得到主题的通知时能够及时更新自己。ConcreteSubject具体主题,在具体主题发生改变时,给所有的观察者发出通知。ConcreteObserver:具体观察者,实现抽象观察者接口,以便本身的状态与主题状态相协调。
-
观察这模式的代码实现
-
创建一个抽象观察者类
/** * 抽象观察者 */ public interface Observer { /** * 观察者需要更新温度、压力和湿度数据 * @param temputure * @param pressure * @param hunidity */ void update(int temputure, int pressure, int hunidity); }
-
创建一个抽象主题类
/** * 抽象主题类 */ public interface Subject { //注册观察者 void registObserver(Observer observer); //移除观察者 void removeObserver(Observer observer); //状态发生改变时通知观察者(监听者) void nitifyObserver(); }
-
创建一个具体的主题类
import java.util.ArrayList; import java.util.List; /** * 具体主题类 */ public class WeatherSubject implements Subject { //通知观察者需要更新的数据 int temputure; int pressure; int hunidity; List<Observer> observerList; public WeatherSubject() { this.observerList = new ArrayList<>(); } /** * 有多个观察者时,不可以依赖特定的通知次序,通知时通过下标的方式实现的, * 如果需要按照特定的顺序进行通知,需要通过优先级队列或者自己实现(排序)的方式将数据一次推入普通队列 * * 从被观察者处推送,将更新的内容推送给观察者 * 还有一种拉数据的机制, * 1. 一旦有数据更新,通知观察者进行数据的拉去(不会直接推送给观察者) * 2. 被观察者提供一个专门的拉取数据的接口,不发送任何数据给观察者,所有的事情由观察者自己解决(已过时) */ @Override public void nitifyObserver() { for (Observer observer : observerList){ observer.update(temputure, pressure, hunidity); } } @Override public void registObserver(Observer observer) { /** * 此处需要判断是否合标,如果合标,则加入观察者队列 * 如果是队列,此处需要根据队列固有的属性,一次推入队列 */ observerList.add(observer); } @Override public void removeObserver(Observer observer) { int i = observerList.indexOf(observer); if(i >= 0){ observerList.remove(i); } } /** * 观察者模式中setWeather兼顾了事件(通知观察者)与事件源(修改类的状态) * 在监听者模式中事件源只包含事件源,对于事件需要专门的类eventObject去处理通知 * 监听者模式多了一个类,这个类多了一个事件 * 如果监听者对象不是惟一的,除了观察以外,还需要多项调用,那么就需要监听者模式, * 将所有调用的函数封装到一个eventObject中,达到解耦的目的,可以通过外观模式一键调用 * @param temputure * @param pressure * @param hunidity */ public void setWeather(int temputure, int pressure, int hunidity) { this.temputure = temputure; this.pressure = pressure; this.hunidity = hunidity; /** * 一旦主题发生了变化,需要马上通知观察者,达到监听的时效性 */ nitifyObserver(); } }
-
创建两个具体的观察者类
/** * 具体的观察者 */ public class ConcreteObserver implements Observer { private Subject weatherData; public ConcreteObserver(Subject weatherData) { this.weatherData = weatherData; weatherData.registObserver(this); } @Override public void update(int temputure, int pressure, int hunidity) { System.out.println("ConcreteObserver接收到最新的天气信息, temputure: " + temputure + ", pressure: " + pressure + ", hunidity: " + hunidity); } }
public class ConcreteObserver2 implements Observer { private Subject weatherData; public ConcreteObserver2(Subject weatherData) { this.weatherData = weatherData; weatherData.registObserver(this); } @Override public void update(int temputure, int pressure, int hunidity) { System.out.println("ConcreteObserver2接收到最新的天气信息, temputure: " + temputure + ", pressure: " + pressure + ", hunidity: " + hunidity); } }
-
创建一个测试类
public class Test { public static void main(String[] args) { WeatherSubject weatherSubject = new WeatherSubject(); Observer observer = new ConcreteObserver(weatherSubject); Observer observer2 = new ConcreteObserver2(weatherSubject); weatherSubject.setWeather(35, 100, 35); } }
-
-
观察者模式的优点
- 两个对象之间松耦合,到他们依然交互,但是不太清楚彼此的细节,观察者模式提供了一种对象设计,让主题和观察者之间松耦合。主题所知道的只有一个观察者列表。每个具体观察者都实现一个抽象观察者接口,主题并不认识任何一个具体的观察者,他只知道所有的具体观察者有一个共同的接口。
- 观察者模式支持"广播通信"。主题会向所有的观察者发出通知。
- 符合"开闭原则"
-
观察者模式的缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,在通知的过程中会花费很多时间。
- 如果观察者和观察目标之间有循环依赖的话,循环调用有可能会造成系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,仅仅知道目标对象发生了变化。
-
观察者模式的使用场景
- 一个抽象模型有两个方面,一个方面依赖于另外一个方面。将这些封装在独立的对象中使他们可以各自独立的改变和使用。
- 一个对象的改变将导致另外的一个或多个对象也发生变化,而不知道具体有多少对象将发生变化,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而不需要知道这些对象是谁。需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,可以使用观察者模式创建一种链式触发机制。
-
JDK中对观察者模式的使用