1. 简介
观察者模式(Observer Pattern),也叫做发布订阅模式(Publish-Subscribe),它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
2. 图解
水果店进口水果销量良好,特别是进口蛇果,目前还没到货,有几个女孩小美、小静、小陶想预定进口蛇果,在到货之前就给水果老板说过,等到货后电话或者微信第一时间通知她们。
3. 案例实现
类图如下
- 定义预定顾客为观察者接口
Observer
,当收到被观察者通知后,执行update()
方法; - 定义水果店水果为被观察者抽象类
FruitObservable
,持有一个类型为Vector
的属性,存放观察者集合,同时定义三个方法addObserver()
,deleteObserver()
,notifyObservers()
,分别为添加预定顾客,删除预定顾客,通知已预订顾客; - 定义
FruitObservable
抽象接口的实现类为进口蛇果ImportedRedDeliciousApple
; - 定义
Observer
接口的实现类为预定进口蛇果的客户类CustomerObserver
;
代码实现如下
package com.wzj.observer.example1;
/**
* @Author: wzj
* @Date: 2019/10/3 14:53
* @Desc: 观察者接口类
*/
public interface Observer {
//收到通知后观察者更新
void update();
}
package com.wzj.observer.example1;
import java.util.Vector;
/**
* @Author: wzj
* @Date: 2019/10/3 14:50
* @Desc: 水果店被观察者类
*/
public abstract class FruitObservable {
//线程安全的容器类
protected Vector obs = new Vector<>();
//添加预定顾客
public void addObserver(Observer o) {
obs.addElement(o);
}
//删除固定顾客
public void deleteObserver(Observer o) {
obs.removeElement(o);
}
//通知已预定顾客
public abstract void notifyObservers();
}
package com.wzj.observer.example1;
/**
* @Author: wzj
* @Date: 2019/10/3 15:19
* @Desc: 进口蛇果类
*/
public class ImportedRedDeliciousApple extends FruitObservable {
@Override
public void notifyObservers() {
System.out.println("通知: 进口蛇果已到...");
for(Observer o : obs) {
//通知每位预定顾客
o.update();
}
}
}
package com.wzj.observer.example1;
/**
* @Author: wzj
* @Date: 2019/10/3 15:25
* @Desc: 预定进口蛇果的客户类
*/
public class CustomerObserver implements Observer {
//观察者姓名
private String name;
public CustomerObserver(String name){
this.name = name;
}
@Override
public void update() {
System.out.println(name + "已收到通知, 将立马过来购买!");
}
}
客户端测试类
package com.wzj.observer.example1;
/**
* @Author: wzj
* @Date: 2019/10/3 15:38
* @Desc:
*/
public class Client {
public static void main(String[] args) {
//进口蛇果
ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple();
//初始化预定客户类
CustomerObserver mei = new CustomerObserver("小美");
CustomerObserver jing = new CustomerObserver("小静");
CustomerObserver tao = new CustomerObserver("小陶");
//将小美、小静、小陶加入水果店的预定客户列表中
apple.addObserver(mei);
apple.addObserver(jing);
apple.addObserver(tao);
//通知客户
apple.notifyObservers();
}
}
执行结果如下
通知: 进口蛇果已到...
小美已收到通知, 将立马过来购买!
小静已收到通知, 将立马过来购买!
小陶已收到通知, 将立马过来购买!
3. JDK中自带实现与源码分析
类图如下
- JDK自带接口
Observer
,当收到被观察者通知后,执行update()
方法; - JDK自带接口
Observable
,boolean
类型的属性changed
表示状态是否变化,持有一个类型为Vector
的属性obs
; - 定义
Observable
抽象接口的实现类为进口蛇果ImportedRedDeliciousApple
; - 定义
Observer
接口的实现类为预定进口蛇果的客户类CustomerObserver
;
代码实现如下
package com.wzj.observer.example2;
import java.util.Observable;
import java.util.Observer;
/**
* @Author: wzj
* @Date: 2019/10/3 17:09
* @Desc: 预定客户类,继承观察者接口
*/
public class CustomerObserver implements Observer {
private String name;
public CustomerObserver(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + "已收到通知, 将立马过来购买!");
}
}
package com.wzj.observer.example2;
import java.util.Observable;
import java.util.Observer;
/**
* @Author: wzj
* @Date: 2019/10/3 17:02
* @Desc: 进口蛇果类,继承jdk的Observable接口
*/
public class ImportedRedDeliciousApple extends Observable {
public void peform() {
System.out.println("通知: 进口蛇果已到...");
//设置状态改变
this.setChanged();
this.notifyObservers();
}
}
客户端代码如下
package com.wzj.observer.example2;
/**
* @Author: wzj
* @Date: 2019/10/3 15:38
* @Desc: 使用jdk自带的Observable类和Observer接口
*/
public class Client {
public static void main(String[] args) {
//进口蛇果
ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple();
//初始化预定客户类
CustomerObserver mei = new CustomerObserver("小美");
CustomerObserver jing = new CustomerObserver("小静");
CustomerObserver tao = new CustomerObserver("小陶");
//将小美、小静、小陶加入水果店的预定客户列表中
apple.addObserver(mei);
apple.addObserver(jing);
apple.addObserver(tao);
//通知客户
apple.peform();
}
}
执行结果
通知: 进口蛇果已到...
小陶已收到通知, 将立马过来购买!
小静已收到通知, 将立马过来购买!
小美已收到通知, 将立马过来购买!
源码分析
/** 被观察者 */
public class Observable {
private boolean changed = false;
private Vector 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) {
Object[] arrLocal;
synchronized (this) {
//状态未改变时返回
if (!changed)
return;
//状态改变后,将集合转为数组
arrLocal = obs.toArray();
//状态恢复初始值
clearChanged();
}
//遍历数组,逐个通知观察者
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
/** 状态改变 */
protected synchronized void setChanged() {
changed = true;
}
4. 观察者模式总结
优点
观察者和被观察者之间是抽象耦合。
不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,所以尽量使用JDK自带的接口。
与责任链结合使用
在很多系统中,Observer模式往往和责任链共同负责对于事件的处理,其中的某一个observer负责是否将事件进一步传递。
缺点
观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。