美文网首页设计模式笔记
设计模式笔记(二): 观察者模式

设计模式笔记(二): 观察者模式

作者: yeonon | 来源:发表于2018-07-21 12:06 被阅读28次

    大部分人每天打开手机都会看到各种APP推送的消息,这个推送是怎么做到的呢?在GUI软件上点击一个按钮,正常情况下都会有一些反应,如何实现的呢?其实这些实现都可以使用观察者模式来实现。

    原理

    观察者模式的原理其实很简单,其核心主要是:

    • 主题
    • 观察者

    主题即观察者关心的东西,例如在APP推送这个场景中,用户是观察者,主题就是APP,用户在安装APP后并开启消息推送功能表明用户关系该APP的消息,当APP有变化的时候,用户就会收到推送。如下图所示:

    关系图

    实现

    实现的关键是明确主题和观察者应该做什么事,主题应该做什么事,主题是会改变的,当发生改变的时候,应该有手段通知到订阅该主题的观察者们,如何做呢?用回调。主题消息改变之后会调用类似notify()的方法,在该方法里调用观察者们的类似update()方法,update()方法是观察者自行实现的,具体处理逻辑完全可以不同。

    一般有两种实现方法,一种是自己手动实现,这需要自己从头到尾编写接口,类等。另一种是依赖java.util包下的Observable类和Observer接口,其中Observable类帮我们实现了很多方法,可以直接使用。

    自己手动实现

    直接上代码即可,有注释:

    public interface Subject {
    
        void registerObserver(Observer observer);
        void removeObserver(Observer observer);
        void notifyObservers();
    }
    
    public interface Observer {
        void update(Object arg);
    }
    
    public class NameSubject implements Subject {
    
        private String name;
    
        //存储订阅该主题的观察者
        private final List<Observer> observers;
    
        public NameSubject() {
            observers = new ArrayList<>();
        }
    
        @Override
        public void registerObserver(Observer observer) {
            observers.add(observer);
        }
    
        @Override
        public void removeObserver(Observer observer) {
            observers.remove(observer);
        }
    
        @Override
        public void notifyObservers() {
            for (Observer observer : observers) {
                observer.update(name);
            }
        }
    
        public void changeName(String name) {
            this.name = name;
            notifyObservers();
        }
    }
    
    public class AObserver implements Observer{
    
        @Override
        public void update(Object arg) {
            System.out.println("A listen arg is : " + arg);
        }
    }
    
    public class BObserver implements Observer{
    
        @Override
        public void update(Object arg) {
            System.out.println("B listen arg is : " + arg);
        }
    }
    
    public class CObserver implements Observer{
    
        @Override
        public void update(Object arg) {
            System.out.println("C listen arg is : " + arg);
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
            NameSubject nameSubject = new NameSubject();
            Observer aObserver = new AObserver();
            Observer bObserver = new BObserver();
            Observer cObserver = new CObserver();
    
            //观察者订阅主题
            nameSubject.registerObserver(aObserver);
            nameSubject.registerObserver(bObserver);
            nameSubject.registerObserver(cObserver);
    
            //推送消息
            nameSubject.changeName("yeonon");
    
            //观察者取消订阅
            nameSubject.removeObserver(aObserver);
            nameSubject.changeName("weiyanyu");
    
        }
    }
    

    启动测试类,可以看到如下输出:

    A listen arg is : yeonon
    B listen arg is : yeonon
    C listen arg is : yeonon
    B listen arg is : weiyanyu
    C listen arg is : weiyanyu
    

    基本符合预期。

    基于JDK实现

    代码如下:

    public class NameSubject extends Observable {
    
        private String name;
    
        public void changeName(String name) {
            this.name = name;
            //调用notifyObservers之前需要调用setChanged方法表示已经改变
            setChanged();
            notifyObservers(this.name);
        }
    }
    
    public class AObserver implements Observer {
    
        private Observable observable;
    
        public AObserver(Observable observable) {
            this.observable = observable;
            this.observable.addObserver(this);
        }
    
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("A listen arg is : " + arg);
        }
    }
    
    //B和C的代码几乎一样,不再贴上来了.....
    public class Main {
    
        public static void main(String[] args) {
            NameSubject nameSubject = new NameSubject();
            Observer aObserver = new AObserver(nameSubject);
            Observer bObserver = new BObserver(nameSubject);
            Observer cObserver = new CObserver(nameSubject);
    
            nameSubject.changeName("yeonon");
            nameSubject.deleteObserver(aObserver);
            nameSubject.changeName("weiyanyu");
    
        }
    }
    
    

    运行测试类,可以看到类似如下输出:

    C listen arg is : yeonon
    B listen arg is : yeonon
    A listen arg is : yeonon
    C listen arg is : weiyanyu
    B listen arg is : weiyanyu
    

    注意到这里顺序和上面手动实现的不太一样,原因是Observable类内部的实现是使用Vector类实现的,而且在遍历Vector的时候是从后往前遍历的。如下代码所示:

    public void notifyObservers(Object arg) {
            /*
             * a temporary array buffer, used as a snapshot of the state of
             * current Observers.
             */
            Object[] arrLocal;
    
            synchronized (this) {
                /* We don't want the Observer doing callbacks into
                 * arbitrary code while holding its own Monitor.
                 * The code where we extract each Observable from
                 * the Vector and store the state of the Observer
                 * needs synchronization, but notifying observers
                 * does not (should not).  The worst result of any
                 * potential race-condition here is that:
                 * 1) a newly-added Observer will miss a
                 *   notification in progress
                 * 2) a recently unregistered Observer will be
                 *   wrongly notified when it doesn't care
                 */
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    

    小结

    现在可以给出观察者模式的定义了:观察者模式定义了对象之间的一对多依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

    使用观察者模式的例子还有很多,例如Netty里使用类似方式实现事件循环,还有channelPipline里的事件传播等。善用观察者模式可以实现很多有意思的功能!

    本系列文章参考书籍是《Head First 设计模式》,文中代码示例出自书中。

    相关文章

      网友评论

        本文标题:设计模式笔记(二): 观察者模式

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