定义
观察者(Observer Pattern)模式也称为发布订阅模式,它是一种在项目中经常使用的模式。
英文原话:Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.
翻译:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,所有依赖于它的对象都会得到通知并被自动更新。
角色
抽象主题(Subject)角色:该角色又称为“被观察者”,可以增加和删除观察者对象。
抽象观察者(Observer)角色:该角色为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
具体主题(Concrete Subject)角色:该角色又称为“具体观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
具体观察者(Concrete Observer)角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。
/** * 抽象主题-被观察者 */ public interface Subject { //登记一个新的观察者 public void attach(Observer observer); //删除一个等级过的观察者 public void detach(Observer observer); //通知所有登记过的观察者对象 public void notifyObserver(); } /** * 抽象观察者 */ public interface Observer { //更新方法 public void update(); } /** * 具体主题 */ public class ConcreteSubject implements Subject { private Vector<Observer> obsVector = new Vector<Observer>(); //登记一个新的观察者 @Override public void attach(Observer observer) { obsVector.add(observer); } //删除一个登记过的观察者 @Override public void detach(Observer observer) { obsVector.remove(observer); } //通知所有登记过的观察者对象 @Override public void notifyObserver() { for (Observer e:obsVector){ e.update(); } } //返回观察者集合的Enumeration对象 public Enumeration<Observer> observers(){ return obsVector.elements(); } //业务方法,改变状态 public void change(){ this.notifyObserver(); } } /** * 具体观察者 */ public class ConcreteObserver implements Observer { @Override public void update() { System.out.println("订阅者一收到通知,进行处理"); } } public class Main { public static void main(String[] args) { //创建一个主题对象(被观察者) ConcreteSubject subject = new ConcreteSubject(); //创建一个观察者 Observer obs = new ConcreteObserver(); //登记观察者 subject.attach(obs); //改变状态 subject.change(); } }
优点
- 观察者和被观察者之间是抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都继承一个抽象观察者接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次,且它们都非常容易扩展。
- 观察者模式支持广播通信。被观察者会向所有登记过的观察者发出通知,这就是一个触发机制,形成一个触发链。
缺点
- 如果一个主题有多个直接或间接的观察者,则通知所有的观察者会花费很多时间,而且它的开发和调试都比较复杂。
- 如果在主题之间有循环依赖的话,被观察者会触发它们进行循环调用,导致系统崩溃。使用观察者模式时要特别注意这一点。
- 如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递的顺序执行。
- 虽然通过观察者模式观察者可以随时知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是怎么发生变化的。
应用场景
- 关联行为场景。
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列的处理机制。
注意事项
- 广播链的问题。一个观察者可以有双重身份,它可以即是观察者也是被观察者,广播链一旦建立,逻辑就比较复杂,可维护性非常差。一般在一个观察者模式中最多出现一个即是观察者也是被观察者的对象,这样消息最多转发一次(传递两次),较易控制。
- 异步处理问题。异步处理就要考虑线程安全和队列的问题。
与责任链模式区别
观察者广播链和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中消息是保持不变的,如果要改变,也只能在原有消息上进行修正。
/** * 朋友圈说说抽象主题 */ public interface ITalkSubject { //注册观察者 public void registerObserver(ITalkObserver observer); //删除观察者 public void delObserver(ITalkObserver observer); //通知所有观察者 public void notifyObserver(); } /** * 抽象观察者 */ public interface ITalkObserver { //接受朋友圈信息,msg为发说说的人 public void updateTalk(String msg); } /** * 具体主题类 */ public class ConcreteTalkSubject implements ITalkSubject{ //观察者集合 List<ITalkObserver> obsList = new ArrayList<ITalkObserver>(); //朋友圈消息 String msg = null; @Override public void registerObserver(ITalkObserver observer) { obsList.add(observer); } @Override public void delObserver(ITalkObserver observer) { obsList.remove(observer); } @Override public void notifyObserver() { if (obsList.size()>0) { for (ITalkObserver obs : obsList) { obs.updateTalk(this.msg); } } } //发送朋友圈消息 public void sendMsg(String msg){ this.msg = msg; this.notifyObserver(); } } /** * 具体观察者 */ public class ConcreteObserver implements ITalkObserver { private String name; public ConcreteObserver(String name) { this.name = name; } //接受消息 @Override public void updateTalk(String msg) { System.out.println(name+"接受消息:"+msg); } } public class Main { public static void main(String[] args) { //被观察者-发说说的人 ConcreteTalkSubject subject = new ConcreteTalkSubject(); ITalkObserver observer = new ConcreteObserver("马云"); //观察者-微信好友 subject.registerObserver(observer); //主题添加观察者 observer = new ConcreteObserver("刘强东"); //观察者-微信好友 subject.registerObserver(observer); subject.sendMsg("也不知道为什么,就是想发条说说~"); } }