设计模式之——观察者设计模式

来源:互联网 发布:如何评价secrets 知乎 编辑:程序博客网 时间:2024/06/06 09:23

目前很火的RxJava其核心就是观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

举个现实中的例子方便大家理解:《每天一杯鲜牛奶》

小区中的住户订奶,他们和奶场签下协议,每天清晨,送奶员就会准时将新鲜的牛奶放在小区的奶箱中。那么,我们这些住户就相当于订阅者(观察者),奶场就是发布者(被观察者),到一定的时间,奶场通知送奶员发货,然后各家各户就自动收到了新鲜的奶了。其实这个协议就是内部的更新接口,奶场只是调用了通知送奶员的方法,然后传参(一杯鲜奶),调用住户的更新接口(奶箱)。

在比如:《作业风云》

老师布置作业,然后全班同学都知道了,老师就是被观察者,学生就是观察者,学生观察老师布置作业的动作,然后自个获取到这个状态就知道布置作业了。

好了,我们来看一下实现:

首先,我们先抽取这个通知的方法,也就是各个观察者实现的方法,具体的通知结果由各个实现者自个处理:

package com.zndroid.dm.ObserverModel.Custom;/** * Created by luzhenyu on 2017/9/11. *//**首先呢,我们抽象出观察者(订阅者),观察者起到更新通知的作用*/public interface Observer {    void update(Object obj);}

我们搞几个观察者:

package com.zndroid.dm.ObserverModel.Custom.impl;import com.zndroid.dm.ObserverModel.Custom.Observer;import com.zndroid.dm.ObserverModel.Custom.Subject;/** * Created by luzhenyu on 2017/9/11. *//**具体实现三个观察者 - 1*/public class Observer1 implements Observer {    @Override    public void update(Object obj) {        System.out.println("Observer1 found " + ((Subject)obj).getName() + " has changed!");    }}
package com.zndroid.dm.ObserverModel.Custom.impl;import com.zndroid.dm.ObserverModel.Custom.Observer;import com.zndroid.dm.ObserverModel.Custom.Subject;/** * Created by luzhenyu on 2017/9/11. *//**具体实现三个观察者 - 2*/public class Observer2 implements Observer {    @Override    public void update(Object obj) {        System.out.println("Observer2 found " + ((Subject)obj).getName() + " has changed!");    }}
package com.zndroid.dm.ObserverModel.Custom.impl;import com.zndroid.dm.ObserverModel.Custom.Observer;import com.zndroid.dm.ObserverModel.Custom.Subject;/** * Created by luzhenyu on 2017/9/11. *//**具体实现三个观察者 - 3*/public class Observer3 implements Observer {    @Override    public void update(Object obj) {        System.out.println("Observer3 found " + ((Subject)obj).getName() + " has changed!");    }}


下面呢,被观察者的行为有:add观察者,delete观察者,notify观察者;这些都是通用的,我们可以抽象出接口

package com.zndroid.dm.ObserverModel.Custom;/** * Created by luzhenyu on 2017/9/11. *//**定义被观察者公用接口*/public interface IObservable {    void add(Observer observer);    void delete(Observer observer);    void notifications();}

下面是被观察者实现,其核心就是内部存储观察者列表,然后提供给外部(客户端)通知变化的方法,具体的变化会通知到观察者列表中每一个观察者具体的更新方法:

package com.zndroid.dm.ObserverModel.Custom;/** * Created by luzhenyu on 2017/9/11. */import java.util.ArrayList;import java.util.List;/**被观察者(发布者)*/public class Subject implements IObservable {    private List<Observer> list;//订阅者列表    private String name;    public Subject() {        list = new ArrayList<>();    }    @Override    public void add(Observer observer) {        System.out.println("Add a observer " + observer.toString());        list.add(observer);//添加订阅者    }    @Override    public void delete(Observer observer) {        System.out.println("Delete a observer " + observer.toString());        list.remove(observer);    }    @Override    public void notifications() {        for (Observer o : list) {            try {                o.update(this);            } catch (Exception e) {                System.out.println("Error");            }//此部分也是JDK中欠考虑的部分        }    }    //通知订阅者发生变化的方法    public void changed() {        System.out.println("Notify observers I'm changed ");        notifications();//通知所有的订阅者‘我’发生变化了    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

下面看一下使用:

/**         * 观察者模式         * 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。         * 其核心就是在抽象的被观察者类中存放观察者们。         *         * 但是如果观察者非常多的话相应的时间也会很长;如果观察者与被观察者有循环依赖的话有可能会造成系统崩溃;         * 观察者并不知道被观察者具体怎么变化的,他们只是知道变化了;如果使用的是Java自带的API,如果其中一个观察者出现更新异常就会影响剩下的观察者。         *         * 现在很火的RxJava、RxAndroid核心就是观察者模式         * 在Java中被观察者继承java.util.Observable,观察者实现java.util.Observer接口就实现了观察者模式。         * */        ////////// 以下是手动实现 //////////        //实例化一个被观察者        Subject subject = new Subject();        subject.setName("我是一个小小的被观察者,好害羞啊");        Observer1 observer1 = new Observer1();        Observer2 observer2 = new Observer2();        Observer3 observer3 = new Observer3();        //添加订阅者        subject.add(observer1);        subject.add(observer2);        subject.add(observer3);        //我要变化咯~~        subject.changed();//所有的订阅者都会收到通知        log("--------------------- Im delete a observer --------------------");        //去掉一个订阅者        subject.delete(observer2);        subject.changed();//所有的订阅者都会收到通知        log("----------------我是分割线-----------------");

运行结果:

[ ======================================== ]
Add a observer com.zndroid.dm.ObserverModel.Custom.impl.Observer1@42a57993
Add a observer com.zndroid.dm.ObserverModel.Custom.impl.Observer2@75b84c92
Add a observer com.zndroid.dm.ObserverModel.Custom.impl.Observer3@6bc7c054
Notify observers I'm changed
Observer1 found 我是一个小小的被观察者,好害羞啊 has changed!
Observer2 found 我是一个小小的被观察者,好害羞啊 has changed!
Observer3 found 我是一个小小的被观察者,好害羞啊 has changed!
[ --------------------- Im delete a observer -------------------- ]
Delete a observer com.zndroid.dm.ObserverModel.Custom.impl.Observer2@75b84c92
Notify observers I'm changed
Observer1 found 我是一个小小的被观察者,好害羞啊 has changed!
Observer3 found 我是一个小小的被观察者,好害羞啊 has changed!
[ ----------------我是分割线----------------- ]
[ ======================================== ]


以上是我们手动实现的,在java中JDK已经有相应的实现了,我们来看一下怎么使用:

在Java中被观察者继承java.util.Observable,观察者实现java.util.Observer接口



比较源码会发现,和我们的思想是一致的:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package java.util;import java.util.Observable;//观察者接口,每个观察者都必须实现的接口,该接口是在被观察的对象发生变化时做出的相应public interface Observer {    void update(Observable var1, Object var2);}

import java.util.Vector;//被观察者类public class Observable {    //这是一个改变标识,来标记该被观察者有没有改变    private boolean changed = false;    //持有一个观察者列表    private Vector 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);    }    //notifyObservers(Object arg)的重载方法    public void notifyObservers() {    notifyObservers(null);    }    //通知所有观察者,被观察者改变了,你可以执行你的update方法了。    public void notifyObservers(Object arg) {        //一个临时的数组,用于并发访问被观察者时,留住观察者列表的当前状态,这种处理方式其实也算是一种设计模式,即备忘录模式。        Object[] arrLocal;    //注意这个同步块,它表示在获取观察者列表时,该对象是被锁定的    //也就是说,在我获取到观察者列表之前,不允许其他线程改变观察者列表    synchronized (this) {        //如果没变化直接返回        if (!changed)                return;            //这里将当前的观察者列表放入临时数组            arrLocal = obs.toArray();            //将改变标识重新置回未改变            clearChanged();        }        //注意这个for循环没有在同步块,此时已经释放了被观察者的锁,其他线程可以改变观察者列表        //但是这并不影响我们当前进行的操作,因为我们已经将观察者列表复制到临时数组        //在通知时我们只通知数组中的观察者,当前删除和添加观察者,都不会影响我们通知的对象        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();    }}

我们尝试使用一下:

package com.zndroid.dm.ObserverModel.Java;import java.util.Observable;/** * Created by luzhenyu on 2017/9/11. */public class TimerOb extends Observable {    private long time;    public long getTime() {        return time;    }    public void setTime(long time) {        this.time = time;    }    public void disPlay() {        setChanged();//Java中必须要先告知状态已改变        notifyObservers();    }}

package com.zndroid.dm.ObserverModel.Java;import java.util.Observable;import java.util.Observer;/** * Created by luzhenyu on 2017/9/11. */public class Listener1 implements Observer {    @Override    public void update(Observable observable, Object o) {        System.out.println(((TimerOb)observable).getTime());    }}

package com.zndroid.dm.ObserverModel.Java;import java.util.Observable;import java.util.Observer;/** * Created by luzhenyu on 2017/9/11. */public class Listener2 implements Observer {    @Override    public void update(Observable observable, Object o) {        System.out.println(((TimerOb)observable).getTime());    }}

package com.zndroid.dm.ObserverModel.Java;import java.util.Observable;import java.util.Observer;/** * Created by luzhenyu on 2017/9/11. */public class Listener3 implements Observer {    @Override    public void update(Observable observable, Object o) {        System.out.println(((TimerOb)observable).getTime());    }}

使用如下:

////////// 下面来看一下Java中自带的实现 //////////        TimerOb timerOb = new TimerOb();        Listener1 listener1 = new Listener1();        Listener2 listener2 = new Listener2();        Listener3 listener3 = new Listener3();        timerOb.addObserver(listener1);        timerOb.addObserver(listener2);        timerOb.addObserver(listener3);        for (int i=0;i<4;i++) {            try {                Thread.sleep(1000);                timerOb.setTime(System.currentTimeMillis());                timerOb.disPlay();            } catch (InterruptedException e) {                e.printStackTrace();            }            log("_________________");        }        log("----------------我是分割线-----------------");

控制台输出,每隔一秒三个订阅者刷新当前时间:

[ ======================================== ]
1505114397015
1505114397015
1505114397015
[ _________________ ]
1505114398015
1505114398015
1505114398015
[ _________________ ]
1505114399015
1505114399015
1505114399015
[ _________________ ]
1505114400015
1505114400015
1505114400015
[ _________________ ]
[ ----------------我是分割线----------------- ]
[ ======================================== ]

【欢迎上码】

【微信公众号搜索 h2o2s2】




原创粉丝点击