观察者模式:
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
认识观察者模式:
可以通过看报纸和杂志订阅来认识观察者模式:
- 报社的业务就是出版报纸。
- 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
- 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
- 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。
出版者 + 订阅者 = 观察者模式
如果你了解报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样:出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observer)。
即:主题(Subject) + 观察者(Observer) = 观察者模式
定义观察者模式:类图
通过一个例子实现观察者模式:当天气发生变化时,通知公告板(可能有很多块)改变天气内容。
分析例子可以知道,此时公告板相当于观察者,天气相当于主题。
主题(Subject) + 观察者(Observer) = 观察者模式
一:自定义观察者模式
创建主题,和观察者,及公告板的显示接口
interface Subject {
//注册观察者
void registerObserver(Observer observer);
//移除(取消注册)观察者
void removeObserver(Observer observer);
//更新观察者
void notifyObserver();
}
interface Observer {
void update(WeatherData weatherData);
}
public interface DisplayElement {
//显示公告
void display();
}
WeatherData
实现Subject,成为主题的实例对象
class WeatherData implements Subject {
private List<Observer> mObservers;
private float t1,t2,t3;//温度,湿度,气压
public WeatherData(){
mObservers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
mObservers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = mObservers.indexOf(observer);
if (i>=0){
mObservers.remove(i);
}
}
@Override
public void notifyObserver() {
for (Observer observer: mObservers){
observer.update(this);
}
}
/**
* 气象站更新天气变化时,通知观察者们
*/
public void measurementsChanged(){
notifyObserver();
}
public void setMeasurements(float t1, float t2, float t3){
this.t1 = t1;
this.t2 = t2;
this.t3 = t3;
measurementsChanged();
}
public float getT1() {
return t1;
}
public float getT2() {
return t2;
}
public float getT3() {
return t3;
}
}
创建显示天气的公告板D1,D2,D3.实现Observer
接口,实现其update
方法更新内容
class D1 implements Observer, DisplayElement {
private float t1, t2;
private Subject mWeathData;//方便以后取消订阅
public D1(Subject weathData) {
this.mWeathData = weathData;
mWeathData.registerObserver(this);
}
@Override
public void update(WeatherData weatherData) {
this.t1 = weatherData.getT1();
this.t2 = weatherData.getT2();
display();
}
@Override
public void display() {
System.out.println("D1:-> 温度:" + t1 + " 湿度:" + t2);
}
}
执行程序:
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
D1 d1 = new D1(weatherData);
D3 d3 = new D3(weatherData);
D2 d2 = new D2(weatherData);
//手动设置天气改变
weatherData.setMeasurements(30, 16, 22);
System.out.println("------------------------");
//手动设置天气改变
weatherData.setMeasurements(20, 18, 18);
System.out.println("------------------------");
//手动设置天气改变
weatherData.setMeasurements(15, 10, 10);
}
运行结果
D1:-> 温度:30.0 湿度:16.0
D3:-> 温度:30.0 湿度:16.0 气压:22.0
D2:-> 温度:30.0 湿度:16.0 气压:22.0
------------------------
D1:-> 温度:20.0 湿度:18.0
D3:-> 温度:20.0 湿度:18.0 气压:18.0
D2:-> 温度:20.0 湿度:18.0 气压:18.0
------------------------
D1:-> 温度:15.0 湿度:10.0
D3:-> 温度:15.0 湿度:10.0 气压:10.0
D2:-> 温度:15.0 湿度:10.0 气压:10.0
上面就是一个完整的观察者模式案例了,当天气发生变化时会调用measurementsChanged()
方法,measurementsChanged会调用notifyObserver
通知观察者自己有数据更新。
二:使用Java内置的观察者模式
其实Java内置了观察者模式,我们也可以不必自己实现Subject和Observer。可以使用Java自带的Observable
可观察的和Observer
观察者。区别是Observable
是一个类而Subject
是一个接口。下面是Java中Observable和Observer源码:
public interface Observer {
void update(Observable o, Object arg);
}
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector<>();
}
/**
* 添加观察者(注册)
*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* 删除观察者
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/**
* 刷新观察者
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* 刷新观察者
*/
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
/*
* 刷新之前要把change设置为false,否则通知发送不下去
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
/**
* 设置change状态
*/
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
可以看出Observer
没有改变,还是只有一个update()
方法,通知观察者更新数据。
Observable
中依然有注册和更新和解除注册的方法,唯一的区别就是添加了状态change
的状态,可以根据Observable 中内容是否发生改变灵活运用或者不用。
此时WeatherData
可以这样写
class WeatherData extends Observable {
private float temperature;//温度
private float humidity; //湿度
private float pressure; //气压
public WeatherData(){}
//天气发生变化
public void measurementsChanged(){
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public float getTemperature() {
return temperature;
}
公告板D1,D2,D3代码
class D1 implements Observer,DisplayElement {
private Observable mObservable;//方便有需要的话取消订阅
private float temperature;
private float humidity;
/**
* 构造方法中订阅
* @param observable
*/
public D1(Observable observable){
this.mObservable = observable;
observable.addObserver(this::update);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData){
WeatherData weatherData = (WeatherData) o;
temperature = weatherData.getTemperature();
humidity = weatherData.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("D1:-> 温度:" + temperature + " 湿度:" + humidity);
}
运行代码:
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
new D1(weatherData);
new D2(weatherData);
new D3(weatherData);
//天气发生变化
weatherData.setMeasurements(10, 10, 23);
System.out.println("--------------------------------");
//天气发生变化
weatherData.setMeasurements(15, 15, 16);
System.out.println("--------------------------------");
//天气发生变化
weatherData.setMeasurements(-1, 5, 6);
}
运行结果:
D3:-> 温度:10.0 湿度:10.0 气压:23.0
D2:-> 温度:10.0 湿度:10.0 气压:23.0
D1:-> 温度:10.0 湿度:10.0
--------------------------------
D3:-> 温度:15.0 湿度:15.0 气压:16.0
D2:-> 温度:15.0 湿度:15.0 气压:16.0
D1:-> 温度:15.0 湿度:15.0
--------------------------------
D3:-> 温度:-1.0 湿度:5.0 气压:6.0
D2:-> 温度:-1.0 湿度:5.0 气压:6.0
D1:-> 温度:-1.0 湿度:5.0
嗯!你注意到差别了吗?再看一次……,(反正我第一次没注意到)
两次代码的执行结果是一样的,只是显示的次数并没有按照第一次那样D1 -> D2 -> D3
。这次是D3 -> D2 -> D1
Java内置的观察模式通知的次序与我们自己实现的观察者次序是相反的,只要不依赖次序,其实也没什么影响。
有多个观察者时,不可以依赖特定的通知次序。
两种方式的区别:
以上就是观察者模式的两种实现方式,一种是实现Subject
接口,一种是继承Observable
类。两者之间的区别就是实现接口的Subject
具备了接口的灵活的特性,而Observable需要继承具有一定的局限性。可以根据实际需求决定采用哪种方式
OO原则:
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦合设计而努力
参考:《Head First设计模式》