观察者模式

Posted yi-hui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察者模式相关的知识,希望对你有一定的参考价值。

定义

观察者模式(又称发布-订阅模式),在这种模式中,一个目标物件管理所有依赖于它的观察者物件,并且在它本身状态改变时主动发出通知,就拿微信和订阅公众号来说,我们订阅了一个公众号,就意味着我们成为了这个公众号的观察者(当然,我们可以订阅许许多多的公众号,也就是说我们可以是很多个目标物件的观察者),这样在每次这个公众号有新的推送的时候(状态改变),这个公众号就会给它的每一个观察者发送新的推送内容。


观察者的一般结构

既然是一般结构,就说明了这种结构可以有其他的变型,不过思想还是这个思想就是了。

  • 抽象主题(Subject):一般是一个接口,规定了具体主题需要实现的方法,如添加和删除观察者,以及通知观察者更新数据。
  • 具体主题(ConcreteSubject):是抽象主题的一个实现类,一般有一个集合用来存放观察者,以便状态变换时通知每一个具体观察者。
  • 观察者(Observer):一个接口,规定了具体观察者更新数据的方法。
  • 具体观察者(ConcreteObserver):是观察者的一个实现类,可以持有一个用来引用存放具体主题的抽象主题的变量,这样就可以通过这个变量让自己加入到具体主题的观察者集合中,或者从集合中删除。


类图

技术分享图片


例子

为了让自己的例子看起来好看一点加了一些乱七八糟的东西。但是,中心思想还是不变的。


import java.util.*;

interface MySubject {
    String getName();
    void add(MyObserver o);
    void remove(MyObserver o);
    void notify(String mes);
}

interface MyObserver {
    String getName();
    void addSubject(MySubject s);
    void removeSubject(MySubject s);
    void update(MySubject s, String mes);
}

class ConcreteSubject implements MySubject {
    private String name;
    private Set<MyObserver> observers;

    public ConcreteSubject(String name) {
        this.name = name;
        observers = new HashSet<>();
    }

    public void add(MyObserver o) {
        System.out.println(name + ": 欢迎" + o.getName() + "关注了本公众号!!!");
        observers.add(o);
    }

    public void remove(MyObserver o) {
        System.out.println(name + ": 很遗憾,"+ o.getName() + "不再关注本公众号了...");
        observers.remove(o);
    }

    public void notify(String mes) {
        for(MyObserver o : observers) {
            o.update(this, mes);
        }
    }

    public String getName() {
        return this.name;
    }
}

class ConcreteObserver implements MyObserver {
    private String name;
    private Set<MySubject> subjects;    //存放所有自己正在观察的主题

    public ConcreteObserver(String name) {
        subjects = new HashSet<>();
        this.name = name;
    }

    public void addSubject(MySubject s) {
        subjects.add(s);    //将新的主题加到自己的主题集合中
        s.add(this);        //将自己加入到该主题的观察者集合中
    }

    public void removeSubject(MySubject s) {
        s.remove(this);     //将自己从该主题的观察者集合中移除
        subjects.remove(s); //将该主题从自己的主题集合中移除
    }

    public void update(MySubject s, String mes) {
        System.out.println(s.getName() + "的订阅者 "+ this.name + " 你好,这是新的推送内容:" + mes);
    }

    public String getName() {
        return name;
    }
}

public class ObserverTest {

    public static void main(String[] args) {
        MySubject s1 = new ConcreteSubject("公众号1");
        MySubject s2 = new ConcreteSubject("公众号2");
        MyObserver o1 = new ConcreteObserver("大娃");
        MyObserver o2 = new ConcreteObserver("二娃");

        o1.addSubject(s1);
        o1.addSubject(s2);
        o2.addSubject(s1);

        s1.notify("今天会下大暴雨...");
        s2.notify("今天是周日,不用上课...");

        o1.removeSubject(s1);

        s1.notify("很遗憾," + o1.getName() + "没有办法接收到这条新的推送了...");
    }
}


Java的内置观察者模式


java用Observable来表示抽象主题,但是这里的Observable是个类而不是接口

package java.util;

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}


观察者(Observer)

public interface Observer {

    void update(Observable o, Object arg);
}

</br/>

虽然内置的观察者模式为我们提供了方便,但是由于抽象主题是个类而非接口,这样又限制我们继承抽象主题后,便不能再继承其他的类,降低了我们设计的灵活性。










以上是关于观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

未调用 LiveData 观察者

Java设计模式补充:回调模式事件监听器模式观察者模式(转)

如何为片段设置观察者

永远观察实时数据的片段

设计模式观察者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

观察者模式