从现实事例讲观察者模式
前言:观察者模式 = 观察者 + 主题
我们先看一个微信公众号的例子。
如上图所示,服务号就是我们的主题
,使用者就是观察者
。现在我们明确下功能:
1、服务号就是
主题
,业务就是推送消息2、
观察者
只需要订阅主题
,只要有新的消息就会送来3、当不想要此
主题
消息时,取消订阅4、只要服务号还在,就会一直有人订阅
观察者模式 = 观察者 + 主题:
为什么这样说呢?我们来看下观察者定义。
观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。
- 上述定义中发生互动的只有
观察者对象
和主题对象
,因此观察者模式
等于他们两个组合相加。
- 上述定义中发生互动的只有
实现主题和观察者
类图:
接下来实现这个类图,包括箭头的意思。
主题 interface,只要实现了 Subject 接口就能成为主题对象
public interface Subject {
//注册观察者,即订阅
public void registerObserver(Observer o);
//移除观察者,即取消关注
public void removeObserver(Observer o);
//主题状态改变,告诉观察者
public void notifyObservers();
}
主题者对象
public class OneSubject implements Subject {
//实现主题对象 ‘一对多’ 功能,即用容器管理所有订阅者。
private ArrayList observers;
//数据状态。当然可以有多个。这是是为了行文方便
private Object object;
//判断是否这个改变是否需要通知观察者。
private Boolean changed;
//初始化容器
public OneSubject() {
observers = new ArrayList();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
//遍历观察者容器,将改变的数据传给每个观察者。
public void notifyObservers(Object object) {
if(changed){
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer)observers.get(i);
observer.update(OneSubject.this, object);
}
changed=false;
}
}
//一旦状态改变,这个方法会被调用
public void measurementsChanged() {
//判断是否满足条件。比如送奶站‘早上’等等。满足才通知观察者
if(condition){
setChanged();
notifyObservers();
}
}
//暴露给外部,用来改变主题状态
public void setMeasurements(Object object) {
this.object=object;
measurementsChanged(object);
}
//暴露给观察者获得数据
public Object getObject(){
return this.object;
}
//重要!!! 此方法有三个意义,可以先看第一条。其余可以返看。
//1.筛选有效通知。只有有效通知可以调用 setChanged。比如微信公众号虽然有人一直在写文章,但只有在写好才发表,而不是发表一个未完成的草稿。
//2.便于撤销通知操作,在主题中,我们可以设置很多次 setChanged,但是在最后由于某种原因需要取消通知,我们可以使用 clearChanged 轻松解决问题。
//3.主动权控制,由于 setChanged 为 protected,而 notifyObservers 方法为 public,这就导致存在外部随意调用 notifyObservers 的可能,但是外部无法调用 setChanged,因此真正的控制权应该在主题这里。
protected synchronized void setChanged() {
changed = true;
}
}
观察者 interface。只要实现了 Observer 接口就能成为观察者对象。
public interface Observer {
public void update(Subject subject);
}
观察者对象实现
public class OneObserver implements Observer {
private Object object;
//初始化一个观察者时同时在主题中注册。
//保存保存一个主题对象的引用方便以后取消注册
public OneObserver(Subject subject) {
subject.registerObserver(this);
}
//接受主题作为参数。根据不同主题更新响应数据。
//现实意义是:小明可以同时向送奶站订牛奶,也可以关注女主播。。。。
public void update(Subject subject) {
//判断是哪个主题。根据主题来调用具体方法。
if (subject instanceof OneSubject) {
OneSubject OneSubject = (OneSubject)subject;
object = OneSubject.getObject();
}
}
观察者模式有什么好处?
- 松耦合
- 观察者增加或删除无需修改主题的代码,只需调用主题对应的增加或者删除的方法即可。
- 主题只负责通知观察者,但无需了解观察者如何处理通知。举个例子,送奶站只负责送递牛奶,不关心客户是喝掉还是洗脸。
- 观察者只需等待主题通知,无需观察主题相关的细节。还是那个例子,客户只需关心送奶站送到牛奶,不关心牛奶由哪个快递人员,使用何种交通工具送达。
- 方便客户端获取最新数据。客户端不必开一个线程时刻等待服务端改变数据。
收获:
- 需要花时间去设计一类事物通用的接口。
- 多用组合。观察者 + 主题。。
- 对日常例子的抽象。目前来看观察者模式可以用生活例子来解释。那么其他模式呢?拭目以待哈哈哈**
- 积累了博客排版的经验。排版累死了。CSDN的Markdown编辑器能上传图片真是太好了,作业部落。。
参考资料:
- 《HeadFirst 设计模式》
- 设计模式 观察者模式 微信公众号为例
- 技术小黑屋:这就是观察者模式