Observer design pattern
观察者模式的概念、观察者模式的结构、观察者模式的优缺点、观察者模式的使用场景、观察者模式的实现示例、观察者模式的源码分析
1、观察者模式的概念
观察者模式,又称为发布-订阅模式,即它定义了一种对象间一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有监听这个主题对象的观察者对象,使它们能够自动更新自己。
2、观察者模式的结构
- 抽象主题:即抽象被观察者,其定义了注册观察者、移除观察者、通知观察者的行为。
- 具体主题:实现抽象主题,实现其定义的注册、移除、通知观察者的行为,并持有一个观察者列表,用来维护监听自身的观察者对象。
- 抽象观察者:定义更新自身的行为。
- 具体观察者:实现抽象观察者,实现其定义的更新自身的行为。
3、观察者模式的优缺点
- 优点:
- 降低了主题与观察者的耦合度,且两者之间是抽象耦合。
- 广播机制,即所有注册进来的观察者都会收到主题的通知。
- 缺点:
- 当观察者过多时,主题通知观察者会较耗时。
- 如果主题存在循环依赖,则主题发送通知会使观察者循环调用,导致系统崩溃。
4、观察者模式的使用场景
- 当对象间存在一对多关系,且一个对象状态的改变会影响其它对象时。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时。
5、观察者模式的实现示例
抽象主题:
public interface Subject {
/**
* 注册观察者
* @param observer
*/
void addObserver(Observer observer);
/**
* 移除观察者
* @param observer
*/
void removeObserver(Observer observer);
/**
* 通知观察者
* @param message
*/
void notifying(String message);
}
具体主题:
public class ConcreteSubject implements Subject {
private List<Observer> observers;
public ConcreteSubject() {
this.observers = new ArrayList<>();
}
@Override
public void addObserver(Observer observer) {
this.observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
this.observers.remove(observer);
}
@Override
public void notifying(String message) {
for (Observer observer : this.observers) {
observer.update(message);
}
}
}
抽象观察者:
public interface Observer {
/**
* 更新
* @param message
*/
void update(String message);
}
具体观察者:
public class OneObserver implements Observer {
private String name;
public OneObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println("观察者 " + this.name + " 收到消息 " + message);
}
}
具体观察者:
public class TwoObserver implements Observer {
private String name;
public TwoObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println("观察者 " + this.name + " 收到消息 " + message);
}
}
测试:
public class ObserverTest {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
subject.addObserver(new OneObserver("zed"));
subject.addObserver(new TwoObserver("fizz"));
subject.addObserver(new TwoObserver("ahri"));
subject.notifying("点击左侧录制按钮 即可开始录制本局比赛");
}
}
测试结果:
观察者 zed 收到消息 点击左侧录制按钮 即可开始录制本局比赛
观察者 fizz 收到消息 点击左侧录制按钮 即可开始录制本局比赛
观察者 ahri 收到消息 点击左侧录制按钮 即可开始录制本局比赛
6、观察者模式的源码分析
java.util.Observer 和 java.util.Observable 这两个接口实现了观察者模式,当我们需要使用观察者模式时只需要实现这两个接口即可。
注,Observer 和 Observable 在新版本中已被弃用,因为其支持的事件模型非常有限,且在多线程环境下会出现问题,所以设计者希望开发者使用 java.util.concurrent 包中的并发数据结构,以确保在线程间实现可靠有序的消息传递。且可以参考 java.util.concurrent.Flow API。
Observer 接口是抽象观察者,定义了更新自身状态的方法。
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an {@code Observable} object's
* {@code notifyObservers} method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the {@code notifyObservers}
* method.
*/
void update(Observable o, Object arg);
}
Observable 接口是抽象主题,即抽象被观察者。它内部用一个 Vector 集合来维护订阅它的观察者,并定义了注册、移除、通知观察者的方法,同时,它还维护了一个 changed 状态,用来表示当前主题是否发生改变,当其为 true 时表示当前主题已发生变化,此时才会通过订阅其的观察者。
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) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
// 当 changed 为 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();
}
// 设置主题变化状态
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();
}
}