设计模式|观察者模式

711 阅读2分钟

观察者模式,也被称为发布订阅模式,还有的人叫它"监听器模式",它是设计模式中行为型模式的一种,近些年开始流行的响应式编程(WebFlux)就是观察者模式的应用之一。

观察者模式的核心思想就是当被观察者(Observable)的状态发生了改变(某一个对象被修改 / 做出某些反应 / 发布一个信息等),会逐一通知监听着这个对象的观察者(Observer)。

观察者 观察 被观察者这个行为称为订阅(subscribe)或 注册(register),被观察者通知观察者观察者的做出的应对称为响应(update)。

举个栗子:我看电视,我即观察者,电视就是被观察者我看电视这个行为称为订阅,电视上放了一段马老师的传统功夫,我立马把电视砸了,这个行为即是响应。这就是简单的一对一的观察者模式。

实际情况是,一个节目出来,世界各地肯定不止一个人在看,所以一个被观察者可以有很多观察者,观察者模式也主要是处理一种 一对多的依赖关系。

观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

现在我们定义一个观察者接口

public interface Observer {
    void update(String event);
}

update方法用于对事件做出响应

一个被观察者父类

public class Observable {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    //通知所有订阅者 执行响应
    public void notifyObservers(String event) {
        for (Observer observer : observers) {
            observer.update(event);
        }
    }
}

用一个list维护观察者列表,addObserver添加观察者, removeObserver移除观察者,notifyObservers取出所有订阅的观察者,逐一通知其更新

现在定义一个电视节目类,继承被观察者父类

public class Television extends Observable{
    public void play(String event) {
        System.out.println("电视正在播放"+ event);
        notifyObservers(event);
    }
}

play方法是电视节目类的行为,并在执行该行为时通知所有订阅者,即看电视的人

定义一个人类观察员,实现观察者接口

public class ManObserver implements Observer{
    @Override
    public void update(String event) {
        System.out.println("收到消息,有新的电视剧上线:"+ event);
    }
}

覆写观察者响应方法,打印一条数据

测试方法:

 public static void main(String[] args) throws Exception {
        Television television = new Television();
        Observer zhangSan = new ManObserver();
        Observer liSi = new ManObserver();
        Observer wanWu = new ManObserver();
        television.addObserver(zhangSan);
        television.addObserver(liSi);
        television.addObserver(wanWu);
        television.play("回村的诱惑");
    }

新建一个电视对象,添加三个人类观察员:张三、李四、王五,这哥仨正躺在床上看电视。

打印如下:

电视正在播放回村的诱惑
收到消息,有新的电视剧上线:回村的诱惑
收到消息,有新的电视剧上线:回村的诱惑
收到消息,有新的电视剧上线:回村的诱惑

三人都收到了消息

当某一个订阅者不需要继续观察时,执行removeObserver()移除该观察者对象即可

在SpringFramework设计中也使用了观察者模式,他们就是事件机制监听器监听器充当订阅者,监听特定的事件;事件源充当被观察的主题,用来发布事件;IOC 容器本身也是事件广播器,可以理解成观察者。

同样的,java源码中已经有了观察者和被观察者的实现,我们无需自己定义:

java.util.Observer 类:

public interface Observer {
    void update(Observable o, Object arg);
}

响应方法的入参改为了范围更广的Object类型,同时把被观察者对象也传了进来

java.util.Observable 类:

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

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

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

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

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

    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!hasChanged())
                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();
    }
}


注意changed相关方法和notifyObservers,只有调用了setChanged()方法使changed为true的时候才会通知订阅者,被观察者也不是以list维护订阅者列表,而是Vector,同时添加了synchronized关键字保证线程安全。