每日一文
已不先定,牧人不正,事用不巧,是谓“忘情失道”;己审先定以牧入,策而无形容,莫见其门,是谓“天神”。
观察者模式
观察者模式(有时又被称为发布-订阅模式、模型-视图模式、源-收听者模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。
类图
1. 订阅者模式
- 观察者实现Observer接口
package com.example.patternproxy.observable;
import android.util.Log;
import java.util.Observable;
import java.util.Observer;
/**
* Created on 2017/3/14.
* Desc:读者,订阅者
* Author:Eric.w
*/
public class Reader implements Observer {
private String name;
public String getName() {
return name;
}
public Reader(String name) {
super();
this.name = name;
}
public void subcreble(String writerName) {
WriterManager.getInstance().getWriter(writerName).addObserver(this);
}
public void unSubcreble(String writerName) {
WriterManager.getInstance().getWriter(writerName).deleteObserver(this);
}
/**
* 被观察者变化时触发的方法(notifyObservers())
*
* @param o
* @param arg
*/
@Override
public void update(Observable o, Object arg) {
if (o instanceof Writer) {
Log.e("pattern", ((Writer) o).getName() + "出新书了,《" + ((Writer) o).getBook() + "》!");
}
}
}
- 被观察者实现Observable接口
package com.example.patternproxy.observable;
import android.util.Log;
import java.util.Observable;
/**
* Created on 2017/3/14.
* Desc:作者,可被订阅
* Author:Eric.w
*/
public class Writer extends Observable {
private String name;
private String book;
public String getBook() {
return book;
}
public String getName() {
return name;
}
public Writer(String name) {
super();
this.name = name;
WriterManager.getInstance().putWriter(this);
}
public void addNewBook(String bookname) {
Log.e("pattern", "public new book :" + bookname);
this.book = bookname;
setChanged();
notifyObservers();
}
}
- 为了更好的管理被订阅者:增加WriterManager,对数据是一个很好的分离
package com.example.patternproxy.observable;
import java.util.HashMap;
/**
* Created on 2017/3/14.
* Desc:作者管理类
* Author:Eric.w
*/
public class WriterManager {
public HashMap<String, Writer> writerMap = new HashMap<>();
public void putWriter(Writer writer) {
writerMap.put(writer.getName(), writer);
}
public Writer getWriter(String writerName) {
return writerMap.get(writerName);
}
/**
* 静态内部类,的静态成员变量只在类加载的时候初始化,直邮调用了getInstance()
* 才会去加载类创建类的实例
*/
public WriterManager() {
}
private static class WriterManagerInstance {
private static WriterManager instance = new WriterManager();
}
public static WriterManager getInstance() {
return WriterManagerInstance.instance;
}
}
2. 事件驱动模式
首先事件驱动模型与观察者模式勉强的对应关系可以看成是,被观察者相当于事件源,观察者相当于监听器,事件源会产生事件,监听器监听事件。所以这其中就搀和到四个类,事件源,事件,监听器以及具体的监听器。
import java.util.HashSet;
import java.util.Set;
//作者类
public class Writer{
private String name;//作者的名称
private String lastNovel;//记录作者最新发布的小说
private Set<WriterListener> writerListenerList = new HashSet<WriterListener>();//作者类要包含一个自己监听器的列表
public Writer(String name) {
super();
this.name = name;
WriterManager.getInstance().add(this);
}
//作者发布新小说了,要通知所有关注自己的读者
public void addNovel(String novel) {
System.out.println(name + "发布了新书《" + novel + "》!");
lastNovel = novel;
fireEvent();
}
//触发发布新书的事件,通知所有监听这件事的监听器
private void fireEvent(){
WriterEvent writerEvent = new WriterEvent(this);
for (WriterListener writerListener : writerListenerList) {
writerListener.addNovel(writerEvent);
}
}
//提供给外部注册成为自己的监听器的方法
public void registerListener(WriterListener writerListener){
writerListenerList.add(writerListener);
}
//提供给外部注销的方法
public void unregisterListener(WriterListener writerListener){
writerListenerList.remove(writerListener);
}
public String getLastNovel() {
return lastNovel;
}
public String getName() {
return name;
}
}
import java.util.EventListener;
public interface WriterListener extends EventListener{
void addNovel(WriterEvent writerEvent);
}
public class Reader implements WriterListener{
private String name;
public Reader(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
//读者可以关注某一位作者,关注则代表把自己加到作者的监听器列表里
public void subscribe(String writerName){
WriterManager.getInstance().getWriter(writerName).registerListener(this);
}
//读者可以取消关注某一位作者,取消关注则代表把自己从作者的监听器列表里注销
public void unsubscribe(String writerName){
WriterManager.getInstance().getWriter(writerName).unregisterListener(this);
}
public void addNovel(WriterEvent writerEvent) {
Writer writer = writerEvent.getWriter();
System.out.println(name+"知道" + writer.getName() + "发布了新书《" + writer.getLastNovel() + "》,非要去看!");
}
}
首先本来是实现Observer接口,现在要实现WriterListener接口,响应的update方法就改为我们定义的addNovel方法,当中的响应基本没变。另外就是关注和取消关注的方法中,原来是给作者类添加观察者和删除观察者,现在是注册监听器和注销监听器,几乎是没什么变化的。
我们彻底将刚才的观察者模式改成了事件驱动,现在我们使用事件驱动的类再运行一下客户端,其中客户端代码和WriterManager类的代码是完全不需要改动的,直接运行客户端即可。我们会发现得到的结果与观察者模式一模一样。
走到这里我们发现二者可以达到的效果一模一样,那么两者是不是一样呢?
答案当然是否定的,首先我们从实现方式上就能看出,事件驱动可以解决观察者模式的问题,但反过来则不一定,另外二者所表达的业务场景也不一样,比如上述例子,使用观察者模式更贴近业务场景的描述,而使用事件驱动,从业务上讲,则有点勉强。
二者除了业务场景的区别以外,在功能上主要有以下区别。
- 观察者模式中观察者的响应理论上讲针对特定的被观察者是唯一的(说理论上唯一的原因是,如果你愿意,你完全可以在update方法里添加一系列的elseif去产生不同的响应,但LZ早就说过,你应该忘掉elseif),而事件驱动则不是,因为我们可以定义自己感兴趣的事情,比如刚才,我们可以监听作者发布新书,我们还可以在监听器接口中定义其它的行为。再比如tomcat中,我们可以监听servletcontext的init动作,也可以监听它的destroy动作。
- 虽然事件驱动模型更加灵活,但也是付出了系统的复杂性作为代价的,因为我们要为每一个事件源定制一个监听器以及事件,这会增加系统的负担,各位看看tomcat中有多少个监听器和事件类就知道了。
- 另外观察者模式要求被观察者继承Observable类,这就意味着如果被观察者原来有父类的话,就需要自己实现被观察者的功能,当然,这一尴尬事情,我们可以使用适配器模式弥补,但也不可避免的造成了观察者模式的局限性。事件驱动中事件源则不需要,因为事件源所维护的监听器列表是给自己定制的,所以无法去制作一个通用的父类去完成这个工作。
- 被观察者传送给观察者的信息是模糊的,比如update中第二个参数,类型是Object,这需要观察者和被观察者之间有约定才可以使用这个参数。而在事件驱动模型中,这些信息是被封装在Event当中的,可以更清楚的告诉监听器,每个信息都是代表的什么。