一、介绍
- 观察者模式也称发布订阅模式,它是一种在项目中经常使用的模式;
- 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新;
二、原理类图
2.1 角色和职责
- 抽象主题(Subject)角色:该角色又称为“被观察者”,可以增加和删除观察者对象。
- 抽象观察者(Observer)角色:该角色为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
- 具体主题(Concrete Subject)角色:该角色又称为“具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
- 具体观察者(Concrete Observer)角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。
2.2 代码分析
- 抽象主题Subject的代码如下所示。
【代码 6-6】 Subject.java
public interface Subject {
//登记一个新的观察者
public void attach(Observer obs);
//删除一个登记过的观察者
public void detach(Observer obs);
//通知所有登记过的观察者对象
public void notifyObserver();
}
- 抽象观察者Observer的代码如下所示。
【代码 6-7】 Observer.java
public interface Observer {
//更新方法
public void update();
}
- 具体主题ConcreteSubject的代码如下所示。
【代码 6-8】 ConcreteSubject.java
public class ConcreteSubject implements Subject {
private Vector<Observer> obsVector = new Vector<Observer>();
//登记一个新的观察者
public void attach(Observer obs){
obsVector.add(obs);
}
//删除一个登记过的观察者
public void detach(Observer obs){
obsVector.remove(obs);
}
//通知所有登记过的观察者对象
public void notifyObserver(){
for(Observer e : obsVector){
e.update();
}
}
//返回观察者集合的Enumeration对象
public Enumeration<Observer> observers(){
return obsVector.elements();
}
//业务方法,改变状态
public void change(){
this.notifyObserver();
}
}
- 具体观察者ConcreteObserver的代码如下所示。
【代码 6-9】 ConcreteObserver.java
public class ConcreteObserver implements Observer{
//实现更新方法
public void update(){
System.out.println("收到通知,并进行处理!");
}
}
- 应用程序Client的代码如下所示。
【代码 6-10】 Client.java
public class Client {
public static void main(String args[]){
//创建一个主题对象(被观察者)
ConcreteSubject subject=new ConcreteSubject();
//创建一个观察者
Observer obs=new ConcreteObserver();
//登记观察者
subject.attach(obs);
//改变状态
subject.change();
}
}
- 运行结果如下所示。
收到通知,并进行处理!
三、优缺点
3.1 优点:
- 观察者和被观察者之间是抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密的耦合在一起,因此它们可以属于不同的抽象化层次,且都非常容易扩展。
- 支持广播通信。被观察者会向所有登记过的观察者发出通知,这就是一个触发机制,形成一个触发链。
3.2 缺点
- 如果一个主题有多个直接或间接的观察者,则通知所有的观察者会花费很多时间,且开发和调试都比较复杂。
- 如果在主题之间有循环依赖,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。
- 如果对观察者的通知是通过另外的线程进行异步投递,系统必须保证投递的顺序执行。
- 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是如何发生变化。
四、应用场景
- 关联行为场景。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。
五、注意事项
- 广播链的问题。一个观察者可以有双重身份,既是观察者也是被观察者,广播链一旦建立,逻辑就比较复杂,可维护性非常差。一般在一个观察者模式中最多出现一个对象既是观察者也是被观察者,这样消息最多转发一次(传递两次),较易控制。
- 异步处理的问题。异步处理就要考虑线程安全和队列的问题
注意:
注意 观察者广播链和责任链模式的最大区别就是观察者广播链在传播的过程中,消息是随时更改的,是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中,消息是保持不变的,如果要改变,也只有在原有消息上进行修正。