设计模式-观察者模式

149 阅读2分钟

观察者模式非常常见,近年来逐渐流行的响应式编程就是观察者模式的应用之一,观察者模式的思想就是一个对象发生一个事件后,逐一通知监听着这个对象的监听者,监听者可以对这个事件马上做出响应。

生活中有很多观察者模式的例子,比如我们平时的开关灯,当我们打开灯的开关,灯马上亮了,当我们关闭灯的关闭时,灯马上就熄灭了,这个过程中,灯就对我们控制开关的事件做出了响应,这就是一个简单的一对一观察者模式。当有一个公众号发表了文章,所有关注了这个公众号的关注者都可以收到这篇文章,这就是所谓的一对多观察者模式。

再举个例子,比如警察一直观察着张三的一举一动,只要张三有什么违法行为,警察马上行动,抓捕张三。

这个过程中:

-警察称为观察者

-张三称为被观察者

-警察观察张三的这个行为称为订阅,或者注册

-张三违法后,警察抓捕张三的行为称为响应

众所周知,张三坏事做尽,是一个法外狂徒,所以不止一个警察会盯着张三,也就是说一个被观察者可以有多个观察者,当被观察者有事件发生时,所有观察者都能收到通知并响应,观察者模式主要处理的是一种一对多的依赖关系,它的定义如下:

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

观察者模式的接口:

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);
        }
    }
}

被观察者中维护了一个观察者列表,提供了三个方法:

-addObserver:将observer对象添加到观察者列表中

-removeObserver:将observer对象从观察者列表中移除

-notifyObserver:通知所有观察者有事件发生,具体实现是调用所有观察者的update方法

有了这两个基类,我们就可以定义出具体的罪犯和警察类。

警察属于观察者:

public class PoliceObserver implements Observer {
    @Override
    public void update(String event) {
        System.out.println("警察收到消息,罪犯在" + event);
    }
}

警察实现了观察者接口,当警察收到事件后,做出响应,这里的响应就是简单打印一条日志。

罪犯属于被观察者:

public class CriminalObservable extends Observable {
    public void crime(String event) {
        System.out.println("罪犯正在" + event);
        notifyObservers(event);
    }
}

罪犯继承自被观察者类,当罪犯有犯罪行为时,所有的观察者都会收到通知

测试:

public class Client {
    @Test
    public void test() {
        CriminalObservable zhangSan = new CriminalObservable();
        PoliceObserver police1 = new PoliceObserver();
        PoliceObserver police2 = new PoliceObserver();
        PoliceObserver police3 = new PoliceObserver();
        zhangSan.addObserver(police1);
        zhangSan.addObserver(police2);
        zhangSan.addObserver(police3);
        zhangSan.crime("放狗咬人");
    }
}

在客户端中,我们new了一个张三,为其添加了三个观察者 可以看到,所有的观察者都可以被通知,当某个观察者不需要继续观察时,调用removeObserver()方法,这就是观察者模式,由于生活中一对多的对象非常常见,所以观察者模式应用广泛。

Java源码中的观察者模式 实际上,Java已经为我们提供了Observable类和Observer类,我们在用到观察者模式时,无需创建这两个基类。

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

Observer类和我们上面的类基本一致,都是只有一个update方法用于响应Observable的事件,区别是有两点:

-update方法将Observable对象提供给了Observer -update的参数类型变成了Object

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();
    }
}

Observable类和我们定义的也是类似的,区别在于:

-用于保存观察者列表的容器不是ArrayList,而是Vector

-添加了一个changed字段,以及setChanged和clearChanged方法,分析可知,当changed字段为true时,才会通知所有观察者,否则不通知观察者,所以当我们使用此类时,想要触发notifyObservers方法,必须先调用setChanged方法,这个字段相当于在被观察者和观察者之间添加了一个可控制的阀门。

-提供了countObservers方法,用于计算观察者的数量

-添加了synchronized关键字保证了线程安全