美文网首页
设计模式笔记五观察者模式

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

作者: summer_lz | 来源:发表于2017-03-14 10:58 被阅读17次

    每日一文

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

    观察者模式

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

    类图


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

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

    相关文章

      网友评论

          本文标题:设计模式笔记五观察者模式

          本文链接:https://www.haomeiwen.com/subject/hbfcnttx.html