观察者模式

开一个新的专题,开始记录关于设计模式的一些学习记录。本来觉得设计模式也就那么回事,看了一些源码,果断被打脸了。认认真真学习吧。

什么是观察者模式

观察者模式是软件设计的一种。在此种模式中,一个目标对象管理所有相依赖于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。


observe.jpg

参与类别

  1. 抽象目标类别
    此抽象类别提供一个界面让观察者进行添附与解附作业。此类别内部有不公开的观察者链,并透过下列方法进行作业。
  • 添附(Attach): 新增观察者到链中,以追踪目标对象的变化。
  • 解附(Detach): 将已经存在的观察者从链中移除。
  • 通知(Notify): 利用观察者所提供的更新函数来通知此目标已经产生变化。
  1. 目标类别
    此类别提供了观察者想要追踪的状态。也利用其源类别(抽象目标类别)所提供的方法,来通知所有的观察者其状态已经更新。此类别拥有如下方法。
  • 取得状态(GetState): 回传目标对象的状态。
  1. 抽象观察者界面
    抽象观察者类别是一个必须被实现的抽象类别。这个类别定义了所有观察者都拥有的更新界面,此界面是用来接收目标类别所发出的更新通知。此类别含有如下的方法。
  • 更新(Update): 会被实现的一个抽象方法。
  1. 观察者类别
    这个类别含有指向目标类别的参数(reference), 以接收来自目标类别的更新状态。此类别含有如下的方法。
  • 更新(Update): 是抽象方法的实现。当这个方法被目标对象调用时,观察者对象将会调用目标对象的取得状态(GetState)方法,来其所拥有的更新目标对象资讯。

每个观察者类别都要实现它的更新方法,以应对状态更新的情形。
当目标对象改变时,会通过调用它自己的通知方法来通知每一个观察者对象,这个通知方法则会去调用已经添附在链中的观察者更新方法。通知与更新方法可能会有一些参数,以方便指明是目前目标对象内的何种改变。这样实现可以增加观察者的效率。

用途

  1. 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的对象内将可允许程序员单独地去变更与重复使用这些对象,而不会产生两者之前交互的问题。
  2. 当其中一个对象的变更会影响其他对象,却又不知道多少对象必须被同时变更时。
  3. 当对象应该有能力通知其他对象,又不应该知道其他对象的实现细节时。

观察者模式通常与MVC范式有关。在MVC中,观察者模式被用来降低model与view的耦合程度。一般而言,modle的改变会触发通知其他身为观察者的model。而这些model实际上是view,Java Swing就是一个范例,示意了modle预期会通过PropertyChangeNotification框架以送出改变的通知给其他的view。Model类别是Java bean类别的一员,并拥有与上述类别目标同样的行为。View类别则关联了GUI中的可视元素,并拥有与上述观察者类别同样的行为。当应用程序在执行时。使用者因view做出相应的更新而看见model所产的变化。

示例

模拟一个微信3D彩票服务号,和一些订阅者。

public interface Observer {

    void update(String msg);
}

public interface Subject {

    /**
     * 注册一个观察者
     * @param observer
     */
    void registerObserver(Observer observer);

    /**
     * 移除一个观察者
     * @param observer
     */
    void removeObserver(Observer observer);

    /**
     * 通知所有观察者
     * @param
     */
    void notifyObserver();
}

public class ObjectFor3D implements Subject {

    private List<Observer> observerList = new ArrayList<>();

    /**
     * 3D彩票号码
     */
    private String msg;

    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int index = observerList.indexOf(observer);
        if (index >= 0) {
            observerList.remove(index);
        }
    }

    @Override
    public void notifyObserver() {
        for (Observer observer: observerList) {
            observer.update(msg);
        }
    }

    public void setMsg(String msg) {
        this.msg = msg;
        notifyObserver();
    }
}
public class Observer1 implements Observer {

    private Subject subject;

    public Observer1(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void update(String msg) {
        System.out.println("observer1 得到 3D 号码 -->" + msg + "  haha");
    }
}

public class Observer2 implements Observer {

    private Subject subject;

    public Observer2(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void update(String msg) {
        System.out.println("observer2 得到 3D 号码 -->" + msg + "  hehie");
    }
}

public class Test {

    public static void main(String[] args) {
        ObjectFor3D subjectFoe3D = new ObjectFor3D();
        Observer1 observer1 = new Observer1(subjectFoe3D);
        Observer2 observer2 = new Observer2(subjectFoe3D);

        subjectFoe3D.setMsg("第一期中奖号码是: 123");
        subjectFoe3D.setMsg("第二期中奖号码是: 456");
    }
}
==============
结果
==============
observer1 得到 3D 号码 -->第一期中奖号码是: 123  haha
observer2 得到 3D 号码 -->第一期中奖号码是: 123  hehie
observer1 得到 3D 号码 -->第二期中奖号码是: 456  haha
observer2 得到 3D 号码 -->第二期中奖号码是: 456  hehie

示例2

感觉上面的例子不好,重新举个栗子。这里,设定一个不同用户接收短信的场景。这里以用户和短信发送发不同的角度分析。用户能够接收短信,首先,要有短息的发送方(假设是国内某个运营商),其次,用户还有接收短信的功能,这两个是最基本的条件,这也就对应上了观察者的。对于运营商来说,它要发短信,首先,它必须知道要发给谁,也就是要有一个用户列表(对应那些观察者),其次,要能够动态管理这些用户,譬如添加新的用户,删除欠费的用户(不让他/她接收短信),最重要的就是发送短信给用户了,这几点就对应了目标对象。下面上代码

/**
 * @Description: 这是一个抽象的观察者
 * @Param:  这里实现一个广播消息机制, Observe就是一个电信用户
 * @Return:
 * @Author: Kevin
 * @Date: 2019-04-19 15:46
 */
public interface Observe {

    String receiveMsg(String msg);
}

/**
 * @Description: 抽象的广播
 * @Param:  这里广播会有添加观察者,删除观察者,通知观察者的机制
 * @Return:
 * @Author: Kevin
 * @Date: 2019-04-19 15:47
 */
public interface Subject {

    void addObserve(Observe observe);

    void deleteObserve(Observe observe);

    void notifyAllOberves();
}

/**
 * @ClassName: MobelSubject
 * @Description: 这里假设是电信公司
 * @Author: kevin
 * @Date: 2019-04-19 15:50
 * @Version: 1.0
 */
public class MobelSubject implements Subject {

    /**
     * 这里是要发送信息的内容
     */
    private String message;

    /**
     * 这是是一个默认发短信的名单
     */
    private List<Observe> observeList = new ArrayList<>();

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void addObserve(Observe observe) {
        observeList.add(observe);
    }

    @Override
    public void deleteObserve(Observe observe) {
        /**
         * 假设是存储位置
         */
        int index = observeList.indexOf(observe);
        if (index >= 0) {
            observeList.remove(index);
        }
    }

    @Override
    public void notifyAllOberves() {
        for (Observe observe: observeList) {
            observe.receiveMsg(this.message);
        }
    }
}

public class MobelObserveOne implements Observe {

    private Subject subject;

    /**
     * 接收的信息内容
     */
    private String msg;

    public MobelObserveOne(Subject subject) {
        this.subject = subject;
        subject.addObserve(this);
    }

    public void showMsg() {
        System.out.println("用户1,收到中国移动服务的信息是: " + this.msg);
    }

    @Override
    public String receiveMsg(String msg) {
        this.msg = msg;
        return this.msg;
    }
}

/**
 * @ClassName: MobelObserveOne
 * @Description: TODO
 * @Author: kevin
 * @Date: 2019-04-19 15:57
 * @Version: 1.0
 */
public class MobelObserveTwo implements Observe {

    private Subject subject;

    /**
     * 接收的信息内容
     */
    private String msg;

    public MobelObserveTwo(Subject subject) {
        this.subject = subject;
        subject.addObserve(this);
    }

    public void showMsg() {
        System.out.println("用户2,收到中国移动服务的信息是: " + this.msg);
    }

    @Override
    public String receiveMsg(String msg) {
        this.msg = msg;
        return this.msg;
    }
}

/**
 * @ClassName: MobelObserveOne
 * @Description: TODO
 * @Author: kevin
 * @Date: 2019-04-19 15:57
 * @Version: 1.0
 */
public class MobelObserveThree implements Observe {

    private Subject subject;

    /**
     * 接收的信息内容
     */
    private String msg;

    public MobelObserveThree(Subject subject) {
        this.subject = subject;
        subject.addObserve(this);
    }

    public void showMsg() {
        System.out.println("用户3,收到中国移动服务的信息是: " + this.msg);
    }

    @Override
    public String receiveMsg(String msg) {
        this.msg = msg;
        return this.msg;
    }
}

/**
 * @ClassName: Test
 * @Description: TODO
 * @Author: kevin
 * @Date: 2019-04-19 16:08
 * @Version: 1.0
 */
public class Test {
    public static void main(String[] args) {
        /**
         * 1 成立电信局
         */
        MobelSubject subject = new MobelSubject();

        /**
         * 2 注册用户
         */
        MobelObserveOne observeOne = new MobelObserveOne(subject);
        MobelObserveTwo observeTwo = new MobelObserveTwo(subject);
        MobelObserveThree observeThree = new MobelObserveThree(subject);

        /**
         * 3 电信局发送短信
         */
        subject.setMessage("欢迎您使用通信服务!!!");
        subject.notifyAllOberves();

        /**
         * 4 用户查看短信
         */
        observeOne.showMsg();
        observeTwo.showMsg();
        observeThree.showMsg();

    }
}
===================
结果
===================
用户1,收到中国移动服务的信息是: 欢迎您使用通信服务!!!
用户2,收到中国移动服务的信息是: 欢迎您使用通信服务!!!
用户3,收到中国移动服务的信息是: 欢迎您使用通信服务!!!

参考链接

观察者模式

设计模式 观察者模式 以微信公众服务为例

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

推荐阅读更多精彩内容