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

大部分人每天打开手机都会看到各种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 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 设计模式》,文中代码示例出自书中。

你可能感兴趣的:(设计模式笔记(二): 观察者模式)