观察者模式及其在Java中的典型应用示例
1、什么是观察者模式
2、观察者模式的特性
3、观察者模式的优缺点及其应用场景
4、观察者模式应用示例
5、观察者模式在JDK和Spring源码中的典型应用示例
1、什么是观察者模式
观察者模式定义:定义对象之间的一对多依赖,让多个观察者对象同时监听某一主题对象,当主题对象发生变化时,它的所有依赖着都会受到通知并更新。观察者模式属于行为型模式。观察者模式的类图示例如下:
如上图所示,主题SubJect需要有添加、删除观察者对象、发布更新消息通知给观察者的功能。当主题有更新时,所有的观察者对象都会得到通知更新。
2、观察者模式的特性
(1)意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
(2)主要解决:
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
(3)何时使用:
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
(4)如何解决:
使用面向对象技术,可以将这种依赖关系弱化。
(5)关键代码:
在抽象类里有一个 ArrayList 存放观察者们。
3、观察者模式的优缺点及其应用场景
(1)优点:
1)观察者和被观察者是抽象耦合的。
2)建立一套触发机制。
(2)缺点:
1)如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
(3)使用场景:
1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3)一个对象必须通知其他对象,而并不知道这些对象是谁。
4)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
(4)注意事项:
1)JAVA 中已经有了对观察者模式的支持类。
2)避免循环引用。
3)如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
4、观察者模式应用示例
(1)示例场景
假设有一个场景主题,该主题多位观察者;该主题每次发布消息时,将通知到每个观察者。那么该主题需要保存、添加、移除观察者对象,并具有通知更新的功能。各个观察者应有一个公共超类或实现一个共同接口。
(2)示例代码
定义一个主题Subject类,并为其添加保存观察者、移除观察者、通知更新功能:
//主题者对象
class Subject{
//保存观察者的容器
private List<Observer> container = new ArrayList<>();
//添加观察者
public void addObserver(Observer observer){
container.add(observer);
}
//删除观察者
public void remove(Observer observer){
container.remove(observer);
}
//更新通知
public void notifyObserver(Object object){
//将更新消息通知到每一个观察者
for(Observer item:container){
item.update(object);
}
}
}
定义观察者接口,并让实际观察者实现该接口:
interface Observer{
//接收更新接口
void update(Object object);
}
//定义一个观察者实体
class Task1 implements Observer{
@Override
public void update(Object object) {
System.out.println("task1 received: "+object);
}
}
class Task2 implements Observer{
@Override
public void update(Object object) {
System.out.println("task2 received: "+object);
}
}
测试示例:
public class ObServerTest {
public static void main(String[] args) {
//测试用例:
//定义主题
Subject subject = new Subject();
//定义观察者并添加观察者对象
Task1 observer1 = new Task1();
Task2 observer2 = new Task2();
subject.addObserver(observer1);
subject.addObserver(observer2);
//进行事件通知
subject.notifyObserver("xxx");
//移除一个事件
subject.remove(observer1);
//再次发布一个事件
subject.notifyObserver("yyy");
}
}
测试结果输出示例如下:
task1 received: xxx
task2 received: xxx
task2 received: yyy
可以看到,主题发布的消息将通知到每一个观察者,但当某个观察者被移除不再监听时,它将不被通知更新。
5、观察者模式在JDK和Spring源码中的典型应用示例
(1)在JDK中的典型应用:Java JDK中的java.util.Observable类就是一个观察者模式的主题类,源码如下:
@Deprecated(since="9")
public class Observable {
private boolean changed = false;
//用来保存观察者对象的容器
private Vector<Observer> obs;
//初始化
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) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
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();
}
}
从源码看出,上述类和我们示例中定义的Subject类功能类似。注:该类在JDK9之后已被声明过时。JDK中的观察者接口是java.util.Observer类。
(2)Spring框架中的典型应用:org.springframework.context.ApplicationListener类,源码如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
//该方法类似于示例中的update方法
void onApplicationEvent(E event);
}
该监听器的主题对象是ApplicationEventMulticaster类的子类,ApplicationEventMulticaster实现了管理观察者和发布时间的接口,Spring应用上下文ApplicationContext可以使用ApplicationEventMulticaster作为实际发布时间的委托。
本文源代码:
https://github.com/JianfuYang/2020-yjf-review/tree/master/src/designpatterns/observer