设计模式之观察者模式

一、观察者模式的定义

定义对象间一对多的依赖关系,使得当前对象改变了状态,则所有依赖于它的对象都会得到通知并自动更新。

二、观察者模式的使用场景

  1. 一个抽象模型有两个方面,其中一个方面依赖于另一个方面;(比如:View依赖于Model的数据,当数据发生改变时,更新View)
  2. 一个对象的改变将导致一个或多个其他对象也发生改变;(比如:同一个数据源对多个观察对象)
  3. 需要在系统创建一个触发链。(比如A影响B,B影响C)

三、UML结构图

观察者模式 UML
  • Subject:抽象主题,也就是被观察者(Observable),把所有的观察者对象的引用保存在一个集合里。每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,会给所有注册过的观察者发出通知notify。
  • Observer:抽象观察者,它定义了更新接口,使得在得到被观察者都更改通知时更新自己。
  • ConcreteObserver:具体观察者,实现了抽象观察者所定义的更新接口。

四、实现1(自己写Observer和Observable)

/*
 * 抽象被观察者,添加删除通知观察者。
 */
public abstract class Subject {
    //保存注册观察者对象
    private Vector<Observer> mObservers = new Vector<>();
    //注册观察者对象
    public void addObserver(Observer o) {
        this.mObservers.add(o);
    }
    //注销观察者对象
    public void removeObserver(Observer o) {
        this.mObservers.remove(o);
    }
    //通知观察者对象
    public void notifyObservers() {
        for (Observer o : this.mObservers) {
            o.update();
        }
    }
}
/*
 * 具体被观察者
 */
public class ConcreteSubject extends Subject {
    public void doSomething() {
        System.out.println("doSomething");
        this.notifyObservers();
    }
}
/*
 * 抽象观察者
 */
public interface Observer {
    void update();
}
/*
 * 具体观察者
 */
public class ConcreteObserver implements Observer {
    @Override
    public void update() {
        System.out.println("收到消息");
    }
}
/*
 * 测试类
 */
public class Client {
    public static void main(String[] args) {
        ConcreteSubject cs = new ConcreteSubject();
        Observer observer1 = new ConcreteObserver();
        Observer observer2 = new ConcreteObserver();
        cs.addObserver(observer1);
        cs.addObserver(observer2);
        cs.doSomething();
    }
}
  • 显示结果:
doSomething
收到消息
收到消息

五、实现2(实际开发中一般是使用java.util包下面的Observer、Observable)

/*
 * 观察者
 */
class EmailUser implements Observer {
    public String name;
    public EmailUser(String name) {
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("亲" + name + "," +
                "你关注的栏目《" + arg + "》已经更新,请及时查看!");
    }
}
/*
 *被观察者
 */
class Emailcolumn extends Observable {
    public void postNewColumn(String content) {
        setChanged();
        notifyObservers(content);
    }
}
/*
 * 测试代码
 */
public class ObserverDemo {
    public static void main(String[] args) {
        Emailcolumn emailcolumn = new Emailcolumn();
        EmailUser user1 = new EmailUser("user1");
        EmailUser user2 = new EmailUser("user2");
        emailcolumn.addObserver(user1);
        emailcolumn.addObserver(user2);
        emailcolumn.postNewColumn("今天最开心:雷佳音");
    }
}
  • 显示结果:
亲user2,你关注的栏目《今天最开心:雷佳音》已经更新,请及时查看!
亲user1,你关注的栏目《今天最开心:雷佳音》已经更新,请及时查看!

六、观察者模式在Android中的实际运用

  1. 回调函数:一对一的关系
  2. ListView数据更新
  3. RxJava
  4. 广播注册
  5. EventBus

1.回调函数

定义:实现了抽象类/接口的实例,实现了父类提供的抽象方法后,将该方法交还给父类来处理。

public class Employee {
    //定义回调接口的成员变量
    private Callback mcallback;
    //声明回调接口
    public interface Callback {
        public abstract void work();
    }
    //设置回调接口对象成员变量
    public void setCallback(Callback callback) {
        this.mcallback = callback;
    }
    //调用回调接口对象中的方法
    public void doWork() {
        if(mcallback!=null){
            mcallback.work();
        }
    }
}
public class Boss {
    private Employee employee;
    //为Employee设置回调函数,在这里定义具体的回调方法
    public void setCallback() {
        employee.setCallback(new Employee.Callback() {
            @Override
            public void work() {
                System.out.println("work");
            }
        });
    }
}

再来看另一个回调的例子:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // do something
    }
});

OnClickListener作为观察者,button作为被观察者,通过setOnClickListener来形成订阅关系。当button被点击时相当于被观察者数据发生改变,就会去通知观察者OnClickListener,这时触发onClick(回调onClick)。

  • 面试题:回调函数和观察者模式的区别?
  • 观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。观察者模式完美的将观察者和被观察的对象分离开,一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
  • 回调函数:其实也算是一种观察者模式的实现方式,回调函数实现的观察者和被观察者往往是一对一的依赖关系。
  • 所以最明显的区别是观察者模式是一种设计思路,而回调函数式一种具体的实现方式;另一明显区别是一对多还是多对多的依赖关系方面。

相关阅读:Android面试一天一题(Day 43:设计模式)

2.ListView的notifyDataSetChanged

当 ListView 的数据发生变化时,调用 Adapter 的 notifyDataSetChanged 函数,这个函数又会调用 DataSetObservable 的 notifyChanged 函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的 onChanged 方法,在 onChanged 函数中又会调用 ListView 重新布局的函数 requestLayout()使得 ListView 刷新界面。

七、观察者模式的缺点

  • 开发效率问题。一个被观察者,多个观察者,开发和调试就会比较复杂。
  • 运行效率问题。多个观察者、多级触发等情况下,因为在Java默认是顺序执行,所以一个观察者卡壳,会影响整体的执行效率,这时候推荐考虑采用异步的方式。

八、观察者模式的优点

  • 观察者和被观察者之间是抽象耦合,观察者和被观察者的扩展比较方便。
  • 建立一套触发机制。例如建立一条触发链。

九、总结

观察者模式主要的作用就是对象解耦,将观察者与被观察者完全隔离,只依赖与 Observer 和 Obserable 抽象。例如LisetView就是运用了Adapter和观察者模式使得它的可扩展性、灵活性非常强,而耦合的很低,这些设计模式在Android源码中优秀运用的典范。

  • 那么如何达到低耦合、高灵活性的代码?
  • 广播接收器和时间总线在观察者模式上有什么相似和不相同的地方?
  • RxJava是怎么运用观察者模式的?

十、参考书籍

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

推荐阅读更多精彩内容

  • 用最简单的一句话来理解观察者模式就是:当一个对象发生改变时,其相关依赖对象皆得到通知并被自动更新。 关于这个图的四...
    芒果味的你呀阅读 1,332评论 0 9
  • 参考 《设计模式:可复用面向对象软件的基础 》5.7 Observer 观察者 对象行为型模式 《设计模式解析》 ...
    WangGavin阅读 496评论 0 1
  • 客户需求 程序设计 一个气象站对应着多个客户端,气象站的数据一发生变化,客户端的数据也要随着更新,这就形成了一种依...
    BlainPeng阅读 985评论 1 17
  • 前言定义:观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到...
    xsp单细胞阅读 444评论 0 1
  • 文|程宜家 “想念曾经最温暖的海底,但大海无边无际,我还能不...
    程宜家阅读 484评论 13 9