观察者模式 : 一支穿云箭,千军万马来相见

小时候一块钱就可以在录像厅看一天电影,印象里最喜欢星爷的电影,特别搞笑。

后来渐渐长大,接触了一些人和事,略微懂了他的那些苦笑、狂笑和欲哭无泪。

我喜欢的演员很多,唯独星爷,能让我看到他的照片,有种想哭的感觉。

星爷,may the god bless you.

最近又看了遍《功夫》,其中有一幕:

阿星装比失败,不曾想引来了斧头帮,斧头帮老大一支穿云箭,真的是千军万马来相见,帅呆了!

骨子里也是个古惑仔的我好崇拜这种场景!太帅了!那,这是怎么做到的呢?

  • 首先我得加入斧头帮,成为斧头帮一份子
  • 然后去登记,有啥事方便通知
  • 等老大一发穿云箭
  • 我跟其他小弟就赶紧出现

用代码实现一下:

1.首先定义 斧头帮老大 AxeGangBoss:

/**
 * description: 斧头帮老大,消息发布者,继承 Observable
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/20/2016
 */
public class AxeGangBoss extends Observable {
    private String mName;

    public AxeGangBoss(String name) {
        mName = name;
    }

    /**
     * 在需要通知小弟的时候调用 setChanged()方法,表明有更新
     * 然后提醒所有小弟
     * @param msg
     */
    public void sendMsg(String msg) {
        //表示有更新
        setChanged();
        notifyObservers(msg);
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }
}

斧头帮老大是消息发布者,当有情况时就调用 sendMsg(…) 方法给小弟发消息。他继承自 Java 源代码提供的 Observable

public class Observable {

    List<Observer> observers = new ArrayList<Observer>();

    boolean changed = false;

    public Observable() {
    }

    public void addObserver(Observer observer) {
        if (observer == null) {
            throw new NullPointerException("observer == null");
        }
        synchronized (this) {
            if (!observers.contains(observer))
                observers.add(observer);
        }
    }

    protected void clearChanged() {
        changed = false;
    }

    public int countObservers() {
        return observers.size();
    }

    public synchronized void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    public synchronized void deleteObservers() {
        observers.clear();
    }

    public boolean hasChanged() {
        return changed;
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    @SuppressWarnings("unchecked")
    public void notifyObservers(Object data) {
        int size = 0;
        Observer[] arrays = null;
        synchronized (this) {
            if (hasChanged()) {
                clearChanged();
                size = observers.size();
                arrays = new Observer[size];
                observers.toArray(arrays);
            }
        }
        if (arrays != null) {
            for (Observer observer : arrays) {
                observer.update(this, data);
            }
        }
    }

    protected void setChanged() {
        changed = true;
    }
}

Observable 里保存着一个 Observer 的集合,然后提供了添加、删除、提醒等方法。其中重要的是 notifyObservers(…) 方法,当确定出事了(changed 为 true)时 ,挨个通知小弟。 Observer 是一个接口,规定了小弟收到消息的方法 update(…):

public interface Observer {
    void update(Observable observable, Object data);
}

2.定义斧头帮小弟 AxeGangPeople,小弟需要按规矩来,实现 Observer 接口:

/**
 * description: 斧头帮小弟,订阅者,实现 Observer 接口
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/20/2016
 */
public class AxeGangPeople implements Observer {
    String mName;

    public AxeGangPeople(String name){
        mName = name;
    }

    /**
     * 必须实现的方法,当消息发布者发出通知后,会调用这个方法
     * @param observable
     * @param data
     */
    @Override
    public void update(Observable observable, Object data) {
        AxeGangBoss writer;
        if (observable instanceof AxeGangBoss) {
            writer = (AxeGangBoss) observable;
            System.out.println(getName() + " 收到来自 " + writer.getName() + " 的消息: " + data.toString());
        }
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }
}

3.然后小弟去老大那登记,老大有消息了就射穿云箭:

    @Test
    public void testSendMsg() throws Exception {
        //创建一个斧头帮老大
        AxeGangBoss shixinzhang = new AxeGangBoss("张拭心");
        //创建两个小弟
        AxeGangPeople niefeng = new AxeGangPeople("聂风");
        AxeGangPeople bujingyun = new AxeGangPeople("步惊云");
        //老大觉得可以,收入麾下
        shixinzhang.addObserver(niefeng);
        shixinzhang.addObserver(bujingyun);
        //老大出事了,赶紧射箭!
        shixinzhang.sendMsg("我有难了,快来救我!");
    }

4.运行结果:

这种 一对多的通知场景 就是传说中的观察者模式

观察者模式

1.定义:

又称“发布-订阅模式”,定义了一种一对多的依赖关系,一个被观察者持有多个观察者对象的引用,当被观察者状态发生改变时,通知所有观察者进行更新。

2.UML 图

  • 其中 Subject 是被观察者接口,即上文提到的 Observable ,持有一个观察者接口集合的引用,定义了观察者的添加、删除以及提醒;
  • ConcreteSubject 实现了被观察者接口,即上文的斧头帮老大 AxeGangBoss,在一定情况下提醒观察者更新;
  • Observer 是观察者抽象类,定义了更新 update 方法;
  • ConcreteObserver 是具体观察者,即上文的小弟 AxeGangPeople,重写 update 方法,进行触发操作。里面有时需要持有一个 Subject 的引用。

3.场景:

有的时候一个状态值会影响许多逻辑,比如电商类软件的位置信息,如果用户切换位置,需要显示对应城市的商品信息、活动、附件的人等等,这时使用观察者模式可以减少对应模块的代码逻辑,直接写好回调方法,等待被回调就好,耦合度降低很多。

4.备注

代码地址点这里

下一篇博客我们通过查看 ListView 源码中的观察者模式 来加深理解。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏令仔很忙

观察者模式和Spring的结合

这周给分了一个任务,就是对查询回来的数据进行各种各样的过滤,有七种不同的过滤条件。过滤条件是在数据库中存着的。在我们项目中有一个热发,就是定时的从数据库中把数...

14920
来自专栏岑玉海

hbase源码系列(九)StoreFile存储格式

从这一章开始要讲Region Server这块的了,但是在讲Region Server这块之前得讲一下StoreFile,否则后面的不好讲下去,这块是基础,Re...

41150
来自专栏程序猿DD

Spring框架中的设计模式(三)

在之前的两篇文章中,我们看到了一些在Spring框架中实现的设计模式。这一次我们会发现这个流行框架使用的3种新模式。 本文将从描述两个创意设计模式开始:原型和...

43070
来自专栏me的随笔

.NET中数据访问方式(一):LINQ

语言集成查询(Language-Integrated Query),简称LINQ,.NET中的LINQ体系如下图所示:

9530
来自专栏微信公众号:Java团长

Java生成、解析二维码

目标:借助Google提供的ZXing Core工具包,使用Java语言实现二维码的生成和解析。

24510
来自专栏苦逼的码农

设计模式走一遍---观察者模式(下)

上篇我们讲解了观察者模式的一些知识,而且自定义观察者模式的经典代码,(传送们:设计模式走一遍---观察者模式)

6820
来自专栏Golang语言社区

[转载]Golang 编译成 DLL 文件

首先撰写 golang 程序 exportgo.go: package main import "C" import "fmt" //export Print...

40240
来自专栏跟着阿笨一起玩NET

.NET深入解析LINQ框架(六:LINQ执行表达式)

在看本篇文章之前我假设您已经具备我之前分析的一些原理知识,因为这章所要讲的内容是建立在之前的一系列知识点之上的,为了保证您的阅读顺利建议您先阅读本人的LINQ系...

7310
来自专栏架构之路

Spring 数据库连接(Connection)绑定线程(Thread)的实现

最近在看spring事务的时候在想一个问题:spring中的很多bean都是单例的,是非状态的,而数据库连接是一种有状态的对象,所以spring一定在创建出co...

39230
来自专栏非典型技术宅

Swift实践:使用CoreData存储多种数据类的通讯录1. CoreData支持存储数据类型2. 使用CoreData存储多种数据类的通讯录3. Codable

20730

扫码关注云+社区

领取腾讯云代金券