模式动机
在软件开发中,我们可能会有这种需求,对象之间建立一种依赖关系,当其中一个对象发生变化时自动通知其他对象,其他对象也做出相应的反应。比如数据库中的触发器,javascript中的onchange()等等。在面向对象中,我们可以通过观察者模式实现类似的功能。在观察者模式中,我们把发生改变的对象称为观察目标,而被通知并做出反应的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何联系,可以根据需要增加和删除观察者。
模式定义
定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被做出相应的反应
模式结构
抽象目标类:Subject,提供注册、删除和通知观察者对象的接口
具体目标类:ConcreteSubject
抽象观察者:Observer,抽象出当目标发生改变时观察者做出反应的接口,
具体观察者:ConcreteObserver
代码示例
假设我们现在有一份数据需要用图表呈现给用户,分别有两种图表,柱状图和饼图。我们希望当数据发生了变化时,能及时通知到图表,让图表重新加载数据。我们用观察者模式实现这个功能。
import java.util.ArrayList;
import java.util.ArrayList;
//抽象目标类
public abstract class Subject {
protected ArrayList<Observer> observers = new ArrayList<Observer>();
//添加一个观察者
public void attach(Observer observer){
observers.add(observer);
}
//移除一个观察者
public void detach(Observer observer){
observers.remove(observer);
}
public abstract void update();
}
//具体目标类
public class Data extends Subject{
@Override
public void update() {
System.out.println("数据发生了改变,数据展现层也记得跟着变哦!");
for(Observer observer:observers){
observer.update();
}
}
}
//抽象观察者类
public interface Observer {
public void update();
}
//具体观察者-柱状图
public class BarChart implements Observer{
@Override
public void update() {
System.out.println("柱状图重新加载数据");
}
}
//具体观察者-饼图
public class PieChart implements Observer{
@Override
public void update() {
System.out.println("饼图重新加载数据");
}
}
//客户端测试类
public class Client {
public static void main(String[] args) {
Subject s = new Data();
Observer o1 = new PieChart();
Observer o2 = new BarChart();
s.attach(o1);
s.attach(o2);
s.update();
s.detach(o2);
s.update();
}
}
//以下为控制台输出
数据发生了改变,数据展现层也记得跟着变哦!
饼图重新加载数据
柱状图重新加载数据
数据发生了改变,数据展现层也记得跟着变哦!
饼图重新加载数据
从上面的例子可以看出,当目标类Data发生变化时,它的观察者类PieChart和BarChart也会做出相应的反应。达到了我们预期的效果
总结
观察者模式的核心是观察目标和观察者,一个目标可以有多个观察者,一旦目标状态发生改变,将会通知所有的观察者,每个观察者也会对应地更新自己的状态,这种交互也叫做“发布-订阅”,目标是消息的发布者,它发出消息时,并不需要具体有哪些观察者,可以有任意个观察者订阅并接收消息。