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

目前很火的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】




猜你喜欢

转载自blog.csdn.net/luzhenyuxfcy/article/details/77934127
今日推荐