观察者模式(发布-订阅模式)
一、概述
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
动机:若将一个系统分割成一系列相互协作的类,需要维护相关对象间的一致性。我们不希望为了维护对象间的一致性而使各类紧密耦合。这样会给维护、扩展和重用都带来了不变。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任何数目的依赖它的Observer,一旦Subject的状态发生变化,所有的Observer都可以得到通知。具体观察者是谁,subject不需知道。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。而且它不知道具体有多少对象有待改变时,应该考虑使用它。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放注册的观察者。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
2、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
总的来说,观察者模式所做的工作其实就是解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化不会影响另一方的变化,这正是依赖倒转原则的体现。
二、实现
例子uml图
观察者模式中的角色:
抽象主题(Subject)角色:抽象主题角色提供维护一个观察者对象聚集的操作方法,对聚集的增加、删除等。
具体主题(ConcreteSubject)角色:将有关状态存入具体的观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色负责实现抽象主题中聚集的管理方法。
抽象观察者(Observer)角色:为具体观察者提供一个更新接口。
具体观察者(ConcreteObserver)角色:存储与主题相关的自洽状态,实现抽象观察者提供的更新接口。
代码实现:代码注解有说明
/**
* 主题或抽象通知者,一般用一个抽象类或一个接口实现。
* 它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何
* 数量的观察者。抽象主题提供一个接口可以增加和删除观察者对象。
*/
import java.util.ArrayList;
import java.util.List;
public abstract class Subject {
private List<Observer> observers = new ArrayList<>();
//增加观察者
public void attach(Observer observer) {
observers.add(observer);
}
//移除观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//通知
public void notifyObserver(String state) {
for (Observer observer : observers) {
observer.update(state);
}
}
public abstract void changeState(String state);
}
/**
* 具体主题或具体通知者,将有关状态存入具体观察者对象,在具体主题
* 的内部状态改变时,给所有注册过的观察者发送通知。
*/
public class ConcreteSubject extends Subject {
private String state;
public String getState() {
return state;
}
public void changeState(String state) {
this.state = state;
System.out.println("主题状态:"+state);
this.notifyObserver(state);
}
}
/**
* 抽象观察者,为所有的具体观察者定义一个接口
* 在得到主题的通知时更新自己。
* 抽象观察者一般用一个抽象类或一个接口实现。
*/
public interface Observer {
/**
* 更新方法
* @param state 更新状态
*/
void update(String state);
//还可以传入主题对象,方便获取相应的主题对象的状态
//void update(Subject);
}
/**
* 具体观察者,实现抽象观察者角色所要求的更新接口,
* 以便使自身的状态与主题的协调。具体观察者也可以
* 保存一个指向具体主题的引用。
*/
public class ConcreteObserver implements Observer {
private String observerState;//观察者状态
/*
*更新观察者的状态,使其跟目标的状态一致
*/
@Override
public void update(String state) {
this.observerState=state;
System.out.println("观察者状态:"+state);
}
}
public class ConcreteObserver2 implements Observer {
private String observerState;//观察者状态
/*
*更新观察者的状态,使其跟目标的状态一致
*/
@Override
public void update(String state) {
this.observerState=state;
System.out.println("观察者2状态:"+state);
}
}
public static void main(String[] args) {
//创建主题
Subject subject = new ConcreteSubject();
//创建观察者对象
Observer observer = new ConcreteObserver();
Observer observer2 = new ConcreteObserver2();
//注册观察者
subject.attach(observer);
subject.attach(observer2);
//改变主题状态
subject.changeState("重启");
}
三、JAVA 中对观察者模式的支持
在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。
1、public interface Observer接口
当一个类想要被告知可观察对象的变化时,它可以实现Observer接口。
源码:
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> 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</code>
* method.
*/
void update(Observable o, Object arg);
}
该接口有个update抽象方法。每当观察对象发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。
2、java.util.Observable类
java.util.Observable提供公开的方法支持观察者对象。其中重要的方法:
setChanged():被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。
notifyObservers():被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
addObserver(Observer o):将观察者添加到此对象的观察者集中,前提是它与集合中已有的某个观察者不同。 未指定将通知发送给多个观察者的顺序。
举例
在教室上计算机课,张三在打游戏,李四在睡觉,他们叫坐在门口的那位同学小刚当老师来的时候通知他们。
例子简单实现;
被观察者(主题)
public class XiaoGang extends Observable {
private String state;
public XiaoGang(List<Observer> list) {
for (Observer o : list) {
addObserver(o);
}
}
public void changeState(String state) {
this.state = state;
setChanged();
notifyObservers(this.state);
}
}
观察者:
public class Lisi implements Observer {
private String state;
@Override
public void update(Observable o, Object arg) {
this.state = (String)arg;
System.out.println("李四,"+state+",别睡觉了");
}
}
public class Zhangsan implements Observer {
private String state;
@Override
public void update(Observable o, Object arg) {
this.state = (String)arg;
System.out.println("张三,"+state+",别打游戏了");
}
}
客户端测试
public static void main(String[] args) {
Lisi ls = new Lisi();
Zhangsan zs = new Zhangsan();
List<Observer> list = new ArrayList<>();
list.add(ls);
list.add(zs);
XiaoGang xg = new XiaoGang(list);
xg.changeState("老师回来了");
}
结果:
张三,老师回来了,别打游戏了
李四,老师回来了,别睡觉了