设计模式之观察者模式
客户需求
/**
* 需求:
*
* 气象站提供最新的天气预报信息,包括: 温度、湿度、气压,这些信息一发生变化, 客户端的信息需要同时更新 请用代码实现模拟实现该功能
*
*
*/
程序设计
一个气象站对应着多个客户端,气象站的数据一发生变化,客户端的数据也要随着更新,这就形成了一种依赖关系,并且是一对多的关系,很显然,我们可以用观察者模式来完成此功能
基本概念
- 定义
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
-
角色
- 抽象被观察者(或者叫主题)
它是一个接口或者抽象类,用来把所有观察者对象的引用保存到一个集合中。定义三个方法:注册观察者、解除观察者、通知观察者;这三个方法就是被观察者与观察者通信的桥梁
- 抽象观察者
为所有的具体的观察者定义的一个接口,定义一个方法:更新;用于获取被观察者发生变化时更新自己
- 具体的被观察者(或者叫主题)
在被观察者内部状态发生变化,通知所有注册过的的观察者。通常实现或继承抽象被观察者
- 具体的观察者
实现抽象观察者接口,使自己与具体的被观察者的状态保持一致
-
UML图
-
DemoOne
-
被观察者
public interface Subject { public void registerObserver(Observer observer); public void unregisterObserver(Observer observer); public void notifyObservers(); }
-
观察者
public interface Observer { public void update(float temp, float humidity, float pressure); }
-
具体被观察者
public class WeatherDataSubject implements Subject { private float temperature; private float humidity; private float pressure; /** * 记录所有的观察者 */ private ArrayList<Observer> observers; public WeatherDataSubject() { observers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void unregisterObserver(Observer observer) { int indexOf = observers.indexOf(observer); if (indexOf >= 0) { observers.remove(indexOf); } } @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { Observer observer = observers.get(i); observer.update(temperature, humidity, pressure); } } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObservers(); } }
-
具体观察者
public class CurrentConditionsDidsplay implements Observer { @Override public void update(float temp, float humidity, float pressure) { System.out.println("temperature=" + temp + "\t humidity=" + humidity + "\t pressure=" + pressure); } }
-
测试
public class ObserverPatternTest { public static void main(String[] args) { WeatherDataSubject subject = new WeatherDataSubject(); CurrentConditionsDidsplay condition = new CurrentConditionsDidsplay(); subject.registerObserver(condition); subject.setMeasurements(23.0f, 1.0f, 350.0f); } }
-
以上这种方式只是简单的实现了被观察者数据发生变化时,主动将数据送过来,是一种推的模式。它不会管观察者到底需不需要全部的数据。但是,有的观察者可能只需要一点点数据,不想收到一堆数据,那此时怎么办呢?那被观察者是否可以提供一个get方法让观察者自己去获取数据,仅通知数据有变化了?接下来我们看看Java内置的观察者模式
- UML图
- DemoTwo
-
具体的被观察者
public class WeatherDataObservable extends Observable { private float temperature; private float humidity; private float pressure; public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; //仅仅是用来通知观察者我的状态发生变化了 setChanged(); notifyObservers(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
-
具体的观察者
public class CurrentConditionsDidsplayObserver implements Observer { @Override public void update(Observable observable, Object obj) { if (observable instanceof WeatherDataObservable) { WeatherDataObservable data = (WeatherDataObservable) observable; float temperature = data.getTemperature(); float humidity = data.getHumidity(); float pressure = data.getPressure(); System.out.println("temperature=" + temperature + "\t humidity=" + humidity + "\t pressure=" + pressure); } } }
-
java内置的观察者模式是属于拉数据模式,被观察者状态发生变化了,通过setChanged方法仅通知观察者状态发生了变化,拉不拉数据是观察者的事了,与被观察者无关了。但是这种设计方式也会有一定的限制:
(1)Observable是一个类,所以我们的子类需继承它,若此时被观察者需同时继承另一个超类,就会陷入两难了,毕竟Java不支持多重继承,这限制了Observable的复用能力
(2)Observable中的setChanged方法被protected了,这意味着:除非你继承Observable类,否则你无法创建Observable实例并组合到你自己的对象中来。“多用组合,少用继承”
知识总结
这两种模式的使用,取决于系统设计时的需要。如果观察者比较复杂,并且观察者进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果观察者比较简单,则“拉模式”就比较适合。