设计模式笔记五观察者模式

2017-03-14  本文已影响17人  summer_lz

每日一文

已不先定,牧人不正,事用不巧,是谓“忘情失道”;己审先定以牧入,策而无形容,莫见其门,是谓“天神”。

观察者模式

观察者模式(有时又被称为发布-订阅模式、模型-视图模式、源-收听者模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。

类图


1. 订阅者模式

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() + "》!");
        }
    }
}
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();
    }
}

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类的代码是完全不需要改动的,直接运行客户端即可。我们会发现得到的结果与观察者模式一模一样。
走到这里我们发现二者可以达到的效果一模一样,那么两者是不是一样呢?
答案当然是否定的,首先我们从实现方式上就能看出,事件驱动可以解决观察者模式的问题,但反过来则不一定,另外二者所表达的业务场景也不一样,比如上述例子,使用观察者模式更贴近业务场景的描述,而使用事件驱动,从业务上讲,则有点勉强。
二者除了业务场景的区别以外,在功能上主要有以下区别。

  1. 观察者模式中观察者的响应理论上讲针对特定的被观察者是唯一的(说理论上唯一的原因是,如果你愿意,你完全可以在update方法里添加一系列的elseif去产生不同的响应,但LZ早就说过,你应该忘掉elseif),而事件驱动则不是,因为我们可以定义自己感兴趣的事情,比如刚才,我们可以监听作者发布新书,我们还可以在监听器接口中定义其它的行为。再比如tomcat中,我们可以监听servletcontext的init动作,也可以监听它的destroy动作。
  1. 虽然事件驱动模型更加灵活,但也是付出了系统的复杂性作为代价的,因为我们要为每一个事件源定制一个监听器以及事件,这会增加系统的负担,各位看看tomcat中有多少个监听器和事件类就知道了。
  2. 另外观察者模式要求被观察者继承Observable类,这就意味着如果被观察者原来有父类的话,就需要自己实现被观察者的功能,当然,这一尴尬事情,我们可以使用适配器模式弥补,但也不可避免的造成了观察者模式的局限性。事件驱动中事件源则不需要,因为事件源所维护的监听器列表是给自己定制的,所以无法去制作一个通用的父类去完成这个工作。
  3. 被观察者传送给观察者的信息是模糊的,比如update中第二个参数,类型是Object,这需要观察者和被观察者之间有约定才可以使用这个参数。而在事件驱动模型中,这些信息是被封装在Event当中的,可以更清楚的告诉监听器,每个信息都是代表的什么。
上一篇下一篇

猜你喜欢

热点阅读