1. 观察者模式简介
在软件开发中,观察者模式是使用频率最高的设计模式之一,如果你做过web开发,对它应该更不会陌生,因为典型的MVC架构就是对观察者模式的一种延伸。在软件开发中经常会碰到这种困境:系统由若干个相互协作的类构成,类之间常有一对多的依赖关系,当被依赖对象的状态变化时,其他所有依赖对象都要发生改变。以MVC为例,模型(Model)对象封装了数据,视图(View)对象对数据进行渲染和进行图形表示。当模型中的数据改变时,视图应该马上得到反馈从而改变其显示的内容。我们需要维护这种具有依赖关系的对象之间的一致性,又不希望为了维护这种一致性导致类之间紧密耦合。而观察者模式正式对这一困境的回答。观察者模式的的最大好处是可以实现具有关联关系的对象之间的解耦,使得双方可以独立的进行扩展和变化,使得系统具有更好的弹性。
2. 观察者模式详解
2.1 观察者模式定义
观察者模式定义了对象之间的一对多依赖关系,每当对象改变状态,所有依赖于它的对象都会得到通知并被自动更新。
2.2观察者模式的结构
观察者模式的结构相对简单,可以用<Head First设计模式>中的一张图来描述

观察者模式的主要角色:
-
Subject(主题)/Observable(被观察者)
通常以抽象类或者接口的形式存在,定义了被观察者即主题必须实现的职责:1.必须能动态的注册和移除观察者 2.在主题状态改变时能通知观察者进行更新。 -
Observer(观察者)
定义了观察者的主要职责:在主题状态改变时需要进行更新,具体的更新逻辑由具体观察者自行实现。 -
ConcreteSubject(具体的主题)/ConcreteObservable(具体的被观察者)
根据业务实际实现抽象主题中定义的接口,并对特定的观察者进行通知。 -
ConcreteObserver(具体的观察者)
根据业务实际实现自己的更新逻辑,在主题状态改变时进行更新。
2.3 观察者模式的简单实现
观察者模式又被称为发布订阅模式,以客户订阅报纸为例,客户相当于观察者,而报社则是被观察者。客户可以向报社订阅报纸,也可以取消订阅。当报社有新报纸出版时,就会将报纸发送给订阅的客户。
- 主题抽象
使用抽象类定义并实现了主题具有的基本职责:添加/移除观察者,在主题状态变化时通知所有注册的观察者。
/** * @author: takumiCX * @create: 2018-10-29 **/ public abstract class Subject { //观察者集合 private CopyOnWriteArrayList<Observer> observers=new CopyOnWriteArrayList<>(); //注册观察者 protected void registerObserver(Observer observer){ observers.add(observer); } //移除观察者 protected boolean removeObserver(Observer observer){ return observers.remove(observer); } /** * 通知观察者 * @param msg 发送给观察者的消息 */ protected void notifyObservers(String msg){ for(Observer observer:observers){ observer.update(msg); } } /** * 通知观察者 */ protected void notifyObservers(){ for(Observer observer:observers){ observer.update(); } } }该抽象类内部维护了一个线程安全的CopyOnWriteArrayList来存储观察者集合,并没有使用Vector或者SynchronizedList等常见同步容器,在需要频繁增删观察者的情况下可以一定程度提升性能。
- 具体的主题实现——报社
/** * @author: takumiCX * @create: 2018-10-29 **/ public class NewsPaperSubject extends Subject { //报纸的期号 private String date; public String getDate() { return date; } public void setDate(String date) { this.date = date; } /** * 通知订阅的客户接收新一期的报纸 */ public void postNewPublication(){ notifyObservers(date); } }具体的主题实现类继承了主题抽象类,并添加了一个状态变量,表示报纸的期号,在通知订阅的客户时需要将该信息也一起传过去。postNewPublication()方法当有新一期的
