设计模式3 观察者模式
作者:互联网
定义
观察者模式(Observer Pattern)定义了一种一对多的依赖关系,这样一来,当一个对象改变状态时,它的所有依赖都会受到通知并自动更新。
——《Head FIRST 设计模式》
观察者模式是JDK中使用最多的模式之一。
AKA:发布-订阅模式(Publish/Subscribe)
主要角色
- 被观察者 Observable/主题Subject:抽象主题角色把所有对观察者对象的引用保存在一个集合(比如ArrayList、Vector)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体的被观察者 Concreate Observable:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
- 观察者 Observer:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
- 具体的观察者 Concreate Observer:存储与被观察者对应的状态。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
如下图(图源:程序员小灰):
举例
源自《Head FIRST设计模式》实现气象站。
气象站提供有温度、湿度、气压等气象数据,如果数据发生了更新,需要随时跟踪获取这些数据,并且display出来。
首先建立接口
//被观察者接口
public interface Subject {
void addObserver(Observer observer);
void removerObserver(Observer observer);
void notifyAllObservers();
}
public interface Observer {
void update(float temp, float humidity);
}
public interface DisplayElement {
void display();
}
实现被观察的WeatherData
public class WeatherData implements Subject{
private float temperature;
private float humidity;
private List<Observer> observerList;
public WeatherData() {
observerList = new ArrayList<Observer>();
}
public void changeWeatherData(float temp,float humidity){
this.temperature = temp;
this.humidity = humidity;
notifyAllObservers();
}
@Override
public void addObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removerObserver(Observer observer) {
int index = observerList.indexOf(observer);
if(index >= 0)
observerList.remove(index);
}
@Override
public void notifyAllObservers() {
for(Observer obs: observerList){
obs.update(temperature,humidity);
}
}
}
实现天气布告板,他作为观察者观察者WeatherData
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private String displayName;
//此处保留了对Subject的引用
private Subject weatherData;
public CurrentConditionsDisplay(String displayName, Subject weatherData) {
this.displayName = displayName;
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void update(float temp, float humidity) {
this.temperature = temp;
this.humidity = humidity;
System.out.println(displayName + "气象布告栏:当前气温" + temperature + "℃,湿度:" + humidity + "%");
}
//Getter&Setter忽略
}
这里保留了对Subject的引用,在实践中是可以有所变化的。
这里的subject接口可以更改为抽象类,在类中定义一些通用的方法。实际上,Java内置的观察者模式就是需要继承自一个抽象类。下面会详细介绍Java内置的观察者模式。
推拉模型下的观察者模式
在上面这种代码运行下,当weather变化时,会将变化通知给观察者。也就是主题对象向观察者推送主题的详细信息,不管观察者是否需要。这种方式是推模型。
但是存在一些需求,观察者想去主动的拉取一些数据下来,而不是被动的被喂数据。先看代码
拉模型下的例子
还是这个例子,如果将逻辑改为拉模式,可以这样修改:
在WeatherData中,不再保留对Subject的引用,改为由参数传入
首先Observer修改接口参数,直接将主题对象(Subject)传入:
public interface Observer {
//注意之前的参数为具体的温度湿度数值,现在是Subject
void update(Subject subject);
}
那么在调用时,需要传入一个Subject的实例
public class WeatherData implements Subject{
private float temperature;
private float humidity;
private List<Observer> observerList;
public WeatherData() {
observerList = new ArrayList<Observer>();
}
public void changeWeatherData(float temp,float humidity){
this.temperature = temp;
this.humidity = humidity;
notifyAllObservers();
}
@Override
public void addObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removerObserver(Observer observer) {
int index = observerList.indexOf(observer);
if(index >= 0)
observerList.remove(index);
}
@Override
public void notifyAllObservers() {
for(Observer obs: observerList){
obs.update(this);
}
}
}
实现void update(Subject)
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private String displayName;
//此处保留了对Subject的引用
private Subject weatherData;
public CurrentConditionsDisplay(String displayName, Subject weatherData) {
this.displayName = displayName;
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void update(Subject subject) {
this.temperature = ((WeatherData)subject).temperature;
this.humidity = ((WeatherData)subject).humidity;
System.out.println(displayName + "气象布告栏:当前气温" + temperature + "℃,湿度:" + humidity + "%");
}
//Getter&Setter忽略
}
推拉模型对比
- 推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。
- 推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,以后随着业务的变化这个方法可能不再适用;拉模型update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要,但是需要将主题对象完全暴露给使用者。
Java内置观察者模式
Java本身有自带实现观察者模式。在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口。
Observer接口
JDK8文档Interface Observer
JDK9已弃用Interface Observer
这个接口只定义了一个方法,即update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。
package java.util;
/**
* A class can implement the <code>Observer</code> interface when it
* wants to be informed of changes in observable objects.
* 当类希望被告知可观察对象的变化时,它可以实现<code>Observer</code>接口
*
* @author Chris Warth
* @see java.util.Observable
* @since JDK1.0
*/
public interface Observer {
*
* @param o the observable object 被观察/主题对象
* @param arg an argument passed to the <code>notifyObservers</code> method.传递给notifyObservers的参数
*/
void update(Observable o, Object arg);
}
Observable类
JDK8文档Class Observable
JDK9已弃用Class Observable
被观察者类都是java.util.Observable类的子类。
核心函数有两个:
- setChanged() :被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。
- notifyObservers():这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
package java.util;
/**
* This class represents an observable object, or "data"
* in the model-view paradigm. It can be subclassed to represent an
* object that the application wants to have observed.
* <p>
* An observable object can have one or more observers. An observer
* may be any object that implements interface <tt>Observer</tt>. After an
* observable instance changes, an application calling the
* <code>Observable</code>'s <code>notifyObservers</code> method
* causes all of its observers to be notified of the change by a call
* to their <code>update</code> method.
* <p>
* The order in which notifications will be delivered is unspecified.
* The default implementation provided in the Observable class will
* notify Observers in the order in which they registered interest, but
* subclasses may change this order, use no guaranteed order, deliver
* notifications on separate threads, or may guarantee that their
* subclass follows this order, as they choose.
* <p>
* Note that this notification mechanism has nothing to do with threads
* and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
* mechanism of class <tt>Object</tt>.
* <p>
* When an observable object is newly created, its set of observers is
* empty. Two observers are considered the same if and only if the
* <tt>equals</tt> method returns true for them.
*/
public class Observable {
private boolean changed = false;
//Vector线程安全
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) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
//如果changed标记是true,就把所有观察者放进一个数组,并把changed标记置为false
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
//通知所有数组里的观察者去update信息
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
//这里是protected,
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();
}
}
在使用时,一个简单的例子实现被观察者
public class Watched extends Observable {
private String state = "";
public String getState() {
return state;
}
//在setter里面通知所有观察者,通过setChanged()函数和notifyObservers()函数
public void setState(String state) {
if (!this.state.equals(state)) {
this.state = state;
setChanged();
}
notifyObservers();
}
}
缺点
Observable是一个类,而Java又不支持多继承。
在JDK9中已弃用,原因是支持Observer和支持的事件模型Observable 非常有限,发送通知的顺序 Observable不明确,状态变化与通知不是一一对应的。
参考
Java设计模式の观察者模式(推拉模型)
观察者设计模式(二) - 推拉模式
漫画:什么是“观察者模式”?
简说设计模式——观察者模式
标签:Observer,void,观察者,模式,humidity,设计模式,public,Subject 来源: https://blog.csdn.net/mxb1234567/article/details/120733766