介绍
观察者模式是一个使用率非常高的模式,这个模式的一个重要作用就是将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。
定义
观察者模式定义对象间一种一对多的依赖关系,使得当每一个对象改变状态时,则所有依赖于它的对象都会得到通知并被自动更新。
使用场景
(1)关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系
(2)事件多级出发场景
(3)跨系统的消息交换场景,如消息队列,事件总线的处理机制
类图
角色介绍:
- Subject - 抽象主题,也就是被观察者(Observable)的角色,抽象主题把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者角色
- ConcreteSubject - 具体主题,也就是具体被观察者(ConcreteObservable)的角色,该角色将有关状态存入具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知
- Observer - 抽象观察者,观察者的抽象类,定义了一个接口,使得在得到主题的更改通知时更新自己
- ConcreteObserver - 具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态改变时更新自己
简单实现
新闻专栏中每天都有新闻更新,但是它的更新消息只有那些订阅了新闻专栏的人才知道,也就是新闻专栏每天都会把新闻发布给订阅用户,这种模式叫做发布---订阅,也就是观察者模式,下面用代码来模拟新闻的发布---订阅过程。
首先要知道的是本例用到的Observer和Observable都是JDK中的内置类型
观察者角色,JDK内置
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}
订阅者,订阅了新闻的人,即具体观察者角色
public class SubScriber implements Observer {
private String name;
public SubScriber(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("你好!" + name + ", 新闻专栏更新啦!" + arg);
}
@androidx.annotation.NonNull
@Override
public String toString() {
return "订阅者: " + name;
}
}
被观察者角色,JDK内置,下面列举了几个主要的方法
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;//观察者列表
public Observable() {
obs = new Vector<>();
}
//注册观察者到观察者列表(即obs)
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
//通知观察者更新
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!hasChanged())//如果内容没有变即标志位为flase,则返回
return;
arrLocal = obs.toArray();//否则将obs转换为数组
clearChanged();//设置标志位为false
}
//遍历观察者,通知它们更新
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
//设置标志位为true
protected synchronized void setChanged() {
changed = true;
}
//设置标志位为false
protected synchronized void clearChanged() {
changed = false;
}
//判单标志位
public synchronized boolean hasChanged() {
return changed;
}
}
新闻专栏,即具体的被观察者角色,当它有更新时通知所有观察者(这里是订阅者)
public class NewsColumn extends Observable {
/**
* 发布新闻
* @param content 新闻内容
*/
public void postNews(String content){
//设置标志位为true,表示有新内容
setChanged();
//通知所有观察者(即订阅了这新闻的人们)
notifyObservers(content);
}
}
测试代码
public class Test {
public static void main(String[] args){
//新闻专栏(被观察者角色)
NewsColumn newsColumn = new NewsColumn();
//订阅者(观察者角色)
SubScriber subScriber1 = new SubScriber("小宁");
SubScriber subScriber2 = new SubScriber("小明");
SubScriber subScriber3 = new SubScriber("李华");
//订阅者订阅新闻(即将观察者注册到被观察者对象的观察者列表中)
newsColumn.addObserver(subScriber1);
newsColumn.addObserver(subScriber2);
newsColumn.addObserver(subScriber3);
//发布新闻
newsColumn.postNews("今天有大事发生!");
}
}
输出结果
你好!李华, 新闻专栏更新啦!今天有大事发生!
你好!小明, 新闻专栏更新啦!今天有大事发生!
你好!小宁, 新闻专栏更新啦!今天有大事发生!
可以看到,当NewsColumn有新闻发布时,会遍历所有Subscriber,通过调用其updata方法给Subscriber发布新闻,这个过程中,通知系统都是依赖Observer和Observable这些抽象类,NewsColumn和Subscriber完全没有耦合,保证订阅系统的稳定性灵活性。而且Observer和Observable都是JDK中的内置类型,可见观察者模式的重要性。
总结
观察者模式主要作用就是对象解耦,通过将观察者和被观察者完全隔绝,只依赖Observer和Observable这些抽象类。