我所理解的观察者模式

本文参考并摘抄了https://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html部分内容

1.概念

观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己

以上摘抄自上述博客
在实际使用过程中,叫观察者模式说法最多,发布/订阅者模式次之,别的说法很少听到,作为了解内容即可。

2.使用场景

脱离了使用场景谈设计模式都是耍流氓
例如:我们每天都要观察时间,到点下班对不对?(到点下班是不可能的,这辈子不可能的,手动悲伤)
在这种场景下,我们每个员工都是观察者,而观察的对象只有一个:时间。这就是典型的观察者模式应用场景。

3.类结构图

下图是你在网上经常能查看到的观察者模式的类结构图


观察者模式UML类图

在理解观察者模式之前,表示被这个名字坑了很久很久很久...
因为听到观察者模式的时候,我感觉这个模式里应该有这么几个对象:

  • 被观察者
  • 观察者
    或者如果按发布/订阅者模式来分:
  • 发布者
  • 订阅者
    在上图中,观察者好理解,Subject是什么角色?
    一种模式有两种命名方式,那么这两种命名方式之间的对应关系是:
  • 被观察者---对应发布者---对应Subject
  • 观察者---对应订阅者

4.实现方式

  • 定义观察者,观察者是一个接口,里面只有一个方法
/**
 * 观察者接口,也就是订阅者接口
 */
public interface MyObserver {
    //更新方法
    void updata();
}
  • 定义被观察者,被观察者是一个普通的java类
/**
 * 被观察者类,也就是Subject类,Publisher类
 */
public class MyObserverable {
    //观察者对象列表
    private ArrayList<MyObserver> observers = new ArrayList<>();
    //添加观察者
    public void attach(MyObserver myObserver) {
        observers.add(myObserver);
    }
    // 去除观察者
    public void detach(MyObserver myObserver) {
        observers.remove(myObserver);
    }
    //通知观察者
    public void notifyObservers() {
        for (MyObserver o : observers) {
            o.updata();
        }
    }
}

观察者模式使用非常广泛,不可能每次使用都需要我们自己去定义相应的类和接口。所以java为我们内置了观察者模式

5.java为我们内置了观察者模式

  • 发布者,也就是被观察者,Observable,是一个类,位于util包下
  • 订阅者,也就是观察者,Observer,是一个接口,同样位于util包下
    Observable中的代码java已经为我们写好,使用的时候直接继承即可,
    源码如下:
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

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

    /**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * Deletes an observer from the set of observers of this object.
     * Passing <CODE>null</CODE> to this method will have no effect.
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to
     * indicate that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and <code>null</code>. In other
     * words, this method is equivalent to:
     * <blockquote><tt>
     * notifyObservers(null)</tt></blockquote>
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to indicate
     * that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            // Android-changed: Call out to hasChanged() to figure out if something changes.
            // Upstream code avoids calling the nonfinal hasChanged() from the synchronized block,
            // but that would break compatibility for apps that override that method.
            // if (!changed)
            if (!hasChanged())
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

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

    /**
     * Clears the observer list so that this object no longer has any observers.
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * Indicates that this object has no longer changed, or that it has
     * already notified all of its observers of its most recent change,
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
     * This method is called automatically by the
     * <code>notifyObservers</code> methods.
     *
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * Tests if this object has changed.
     *
     * @return  <code>true</code> if and only if the <code>setChanged</code>
     *          method has been called more recently than the
     *          <code>clearChanged</code> method on this object;
     *          <code>false</code> otherwise.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#setChanged()
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Returns the number of observers of this <tt>Observable</tt> object.
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
        return obs.size();
    }

Observer是一个接口,源码如下:

public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

6.怎么使用

以本篇“使用场景”中的例子来说明:

/**
 * 工作时间类
 * 也就被观察者类/发布者类
 * 继承自java的Observable类
 */
public class WorkTimer extends Observable {

    //这个类里有关观察者模式需要的方法都被封装在了Observable中
    //如果不需要处理别的逻辑,那么这个类只是一个空类
}
/**
 * 普通员工类
 * 也就是观察者类/订阅者类
 * 实现Observer接口
 */
public class Employee implements Observer {
    private static final String TAG = "Employee";

    @Override
    public void update(Observable o, Object arg) {
        //收到更新提示后,即可在这个方法中处理自己的逻辑
        //参数1:被观察者类
        //参数2:任意参数,一般跟业务相关的数据
        //java在这个参数中返回了被观察者,是为了预防我们有别的需要
        Log.d(TAG, "update: 我是普通员工,到点就要下班");
        //例如,我只希望收到一次通知
        o.deleteObserver(this);
    }
}
/**
 * 经理类,属于观察者
 */
public class Manager implements Observer {
    private static final String TAG = "Manager";

    @Override
    public void update(Observable o, Object arg) {
        Log.d(TAG, "update: 我是经理,到点后还得开会");
    }
}

使用代码示例:

      //初始化被观察者类
        WorkTimer timer = new WorkTimer();
        //初始化普通员工类
        Employee employeeFrist = new Employee();
        EmployeeSecond employeeSecond = new EmployeeSecond();
        //初始化经理类
        Manager manager = new Manager();

        //添加到观察者列表
        timer.addObserver(employeeFrist);
        timer.addObserver(employeeSecond);
        timer.addObserver(manager);

        //被观察者更新
        timer.notifyObservers("2018年11月");

        //后来,Manager离职了,将其从观察者列表中去掉
        timer.deleteObserver(manager);
        //更新的时候,经理类不会再收到提示
        timer.notifyObservers("2018年12月");

写在最后

在观察者模式中,概念有些模糊,而网上的人很少(自少我没有看到)把这几个概念严格对应起来的人,下面专门说明一下,相信你会有些收获。
被观察者(Observable),也叫发布者(publisher),也叫主题(Subject)
观察者(Observer),也就是订阅者(Subscribe)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,053评论 25 707
  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,741评论 0 14
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,711评论 2 59
  • 幽梦难圆旧日欢 悲苦思恋皆惘然 惟愿天公降慈悲 来生再续未了缘
    吃了一个惊阅读 234评论 2 4
  • 同样是一鼓作气,为什么别人花了很少的时间完成了,而我却用了很多时间?同样是一件事情,为什么别人比我少了两倍的时间?...
    一叶繁华_bc31阅读 629评论 1 2