观察者模式
2016-12-8 11:08:06
观察者模式又叫发布-订阅模式,它定义了一种一对多的依赖关系,多个观察者对象可同时监听某一主题对象,当该主题对象状态发生变化时,相应的所有观察者对象都可收到通知。这是一个非常好理解的模式,简单介绍如下:
场景
假设我们有这样一个需求,我们的新闻博客写的不错,有几个读者向订阅我们的新闻。这样以便于我们的博客一有新闻更新,就可以推送到用户了。简单的我们可以这么写
但是我们发现如果我们的用户Amy
对新闻内容不满意,想退订新闻的话,我们的发布新闻代码得更改,同时我们的新用户Eric
想要订阅,代码又得重新改,这显然违背了开闭原则。这样做的问题有:
- 我们是针对具体实现编程,而非针对接口。
- 对于每个新的用户,我们都得修改代码。
- 我们无法再运行时动态地增加(或删除)用户。
- 用户没有实现一个公共的接口。所有用户都是update方法
- 我们尚未封装改变的部分。
观察者模式
这个时候我们的观察者模式就登场了,观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。观察者模式一般会有如下角色:
抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。 它的类图如下:
松耦合的威力
观察者模式实现了松耦合,就如上文的例子,新闻的发布者和用户之间会有交互,但他们不需要清楚彼此的细节。 当两个对象之间松耦合,他们依然可以互交,但是不太清楚彼此的细节。 观察者模式提供了一种对象设计,让主题和观察者之间松耦合。 关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类是谁,做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用心的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者。有新类型的观察者出现时,主题的代码不需要修改,假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,人后注册为观察者即可,主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
新闻发布代码实现
Subject.java
主题的抽象接口
Observer.java
观察者的抽象接口
NewsPublisher.java
新闻发布者--主题的具体类
NewsRegisterJack.java
注册用户jack
NewsRegisterAmy.java
注册用户amy
我们来做一下测试:
输出如下
观察者模式优缺点
观察者模式优点
- 抽象主题只依赖于抽象观察者
- 观察者模式支持广播通信
- 观察者模式使信息产生层和响应层分离
观察者模式缺点
如一个主题被大量观察者注册,则通知所有观察者会花费较高代价 如果某些观察者的响应方法被阻塞,整个通知过程即被阻塞,其它观察者不能及时被通知
观察者模式与OOP原则
已遵循的原则
- 依赖倒置原则(主题类依赖于抽象观察者而非具体观察者)
- 迪米特法则
- 里氏替换原则
- 接口隔离原则
- 单一职责原则
- 开闭原则
参考
Head First设计模式读书总结——观察者模式 设计模式——观察者模式:天气推送的两种实现 观察者模式中,消息采用推和拉方式来传递的比较