文章目录

  • 一、简介
  • 二、实现
    • 1、一对一的耦合实现
    • 2、一对一的手动耦合实现
    • 3、分析一下为什么叫完全解耦?
    • 4、一对多的实现
    • 5、抽象出来 + 注解 (干货)
  • 三、相关源码
    • 1、RxJava
    • 2、生命周期组件 Lifecycle
  • 四、小结

一、简介

Android 中,一提到 观察者模式(Observer Pattern),肯定联想到 RxJava,这种响应式编程号称是十分解耦的操作,对于这样一个牛逼的设计模式,我们很有理由要好好学习之。

定义: 一个对象通过 “观察” 另一个对象的某个行为,而进行响应操作。

使用场景:如果 观察者模式 用于对象之间存在一对多的关系时,能够发挥最大的优势。在 RecyclerView 中,如果数据发生变化,需要通知列表更新UI;通过观察 Avtivity 的生命周期,来管理一些对象或者逻辑。

实现要素:理解 被观察者观察者订阅 三个概念。

  1. 被观察者: 产生某个事件。
  2. 观察者: 收到 被观察者 的某个事件,从而进行一些处理。
  3. 订阅: 观察者 通过 订阅被观察者,才能收到 被观察者 产生的事件。

观察者模式 清晰的划分两个角色—— 被观察者观察者,这两者完全可以各自独立,不会产生耦合,唯有通过 订阅 这个操作,才会将两个耦合在一起。被观察者 可以自己一个人拼命的产生事件,当我想要得到这个事件的时候,我随时可以将 观察者 通过 订阅 来接收 被观察者 产生的事件。

二、实现

以报社发布新闻,人们订阅报社为例。

1、一对一的耦合实现

class Newspaper(private val person: Person) {fun publishNews(content:String){person.read(content)}
}class Person{fun read(content: String) {println(content)}
}fun main() {val person = Person()val newspaper = Newspaper(person)newspaper.publishNews("特朗普为抵御新冠病毒,每日一瓶敌敌畏!")
}

如上所示,Newspaper 就是作为被观察者Person 就是一个观察者,因为在 Newspaper 的构造方法中强耦合了一个 Person 对象,所以我们没有看到 订阅 这个概念。那么接下来就展示 订阅 的概念。

2、一对一的手动耦合实现

分别有两种不同的表达(两个方法二选一),但是作用都是一样的,都是为了初始化 被观察者 中的 观察者

class Newspaper() {var person:Person? = null/***  <方法1>*/fun addObserver(person:Person){this.person = person}fun publishNews(content:String){person?.read(content)}
}class Person{/***  <方法2>*/fun subscribe(newspaper: Newspaper){newspaper.person = this}fun read(content: String) {println(content)}
}fun main() {val person = Person()val newspaper = Newspaper()//方法1    被观察者添加观察者newspaper.addObserver(person)//方法2 观察者订阅被观察者person.subscribe(newspaper)newspaper.publishNews("特朗普为抵御新冠病毒,每日一瓶敌敌畏!")
}

3、分析一下为什么叫完全解耦?

想象一下,一家报社每天不停的发新闻,就算没有一个读者,它也不会断更,他不会等待有了第一个读者后,才开始更新。这时候报社的更新行为,与读者没有任何的关联,但是当读者订阅了报社之后,报社的每次新闻内容,就能够传达给读者。

还是看上面的例子,我修改一下 main 函数:

fun main() {val person = Person()val newspaper = Newspaper()var i = 1Thread {while (i < 12) {println("报社发布第${i}个月的新闻了:")newspaper.publishNews("特朗普为抵御新冠病毒,每日一瓶敌敌畏!")i++Thread.sleep(200)}}.start()Thread {while (i < 12) {//等 i == 6 的时候,再使得读者订阅报社if (i == 6) {person.subscribe(newspaper)}else{Thread.yield()}}}.start()
}

看打印日志:

报社发布第1个月的新闻了:
报社发布第2个月的新闻了:
报社发布第3个月的新闻了:
报社发布第4个月的新闻了:
报社发布第5个月的新闻了:
报社发布第6个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!
报社发布第7个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!
报社发布第8个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!
报社发布第9个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!
报社发布第10个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!
报社发布第11个月的新闻了:
特朗普为抵御新冠病毒,每日一瓶敌敌畏!

4、一对多的实现

既然是报社,不可能是只为一个人服务的,而是为千千万万个读者服务的。所以我们可以在 Newspaper 中创建一个数组,来维护读者们。

class Newspaper() {private val persons = mutableListOf<Person>()fun addObserver(person: Person){if (!persons.contains(person)){persons.add(person)}}fun removeObserver(person: Person){persons.remove(person)}fun publishNews(content: String) {persons.forEach { person ->person.read(content)}}
}class Person(val name:String) {fun read(content: String) {println("${name}阅读了:${content}")}
}fun main() {val person1 = Person("张三")val person2 = Person("李四")val newspaper = Newspaper()newspaper.addObserver(person1)newspaper.publishNews("后浪们涌起摆摊热潮,程序员们或将辞职摆摊!")newspaper.addObserver(person2)newspaper.publishNews("是什么让程序员变得更强?或许是因为发型!")newspaper.removeObserver(person2)newspaper.publishNews("报社绩效不佳,读者流失惨重!")
}

打印内容:

张三阅读了:后浪们涌起摆摊热潮,程序员们或将辞职摆摊!
张三阅读了:是什么让程序员变得更强?或许是因为发型!
李四阅读了:是什么让程序员变得更强?或许是因为发型!
张三阅读了:报社绩效不佳,读者流失惨重!

5、抽象出来 + 注解 (干货)

接下来看一个骚操作,Talk is cheap,show you code:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class PublishEvent {}interface Observer {}class Person(val name: String) : Observer {@PublishEventfun read(content: String) {println("${name}阅读了:${content}")}fun copy(content: String){println("${name}抄写了:${content}")}@PublishEventfun relay(content: String){println("${name}转发了:${content}")}
}class Newspaper() {private val persons = mutableListOf<Observer>()fun addObserver(person: Observer) {if (!persons.contains(person)) {persons.add(person)}}fun removeObserver(person: Observer) {persons.remove(person)}fun publishNews(content: String) {persons.forEach { person ->val publishEventMethods = person.javaClass.methods.filter { method ->method.annotations.find { annotation ->annotation is PublishEvent} != null}.filter { it.parameterTypes.size == 1 && it.parameterTypes[0] == String::class.java }publishEventMethods.forEach { method ->method.invoke(person, content)}}}
}fun main() {val person = Person("张三")val newspaper = Newspaper()newspaper.addObserver(person)newspaper.publishNews("后浪们涌起摆摊热潮,程序员们或将辞职摆摊!")newspaper.publishNews("是什么让程序员变得更强?或许是因为发型!")newspaper.publishNews("报社绩效不佳,读者流失惨重!")
}

打印内容:

张三阅读了:后浪们涌起摆摊热潮,程序员们或将辞职摆摊!
张三转发了:后浪们涌起摆摊热潮,程序员们或将辞职摆摊!
张三阅读了:是什么让程序员变得更强?或许是因为发型!
张三转发了:是什么让程序员变得更强?或许是因为发型!
张三阅读了:报社绩效不佳,读者流失惨重!
张三转发了:报社绩效不佳,读者流失惨重!

这样写有什么好处呢?

这样写的好处就是:不仅将 被观察者观察者 解耦了,还把书写代码的过程也解耦了,这意味着可以使两个开发者分开完成同一件事情,一个人写 被观察者 的实现(加上一个注解),另一个人写 观察者 的实现(使用注解)。

这在比较大型的框架中有很好的实用性。例如一个团队开发 IM 模块,一批人写 Socket 并且通过注解定义消息类型,设定一个发送事件源的 被观察者 ,另一批人根据定义好的注解,写带有具体业务逻辑的 观察者,两者之间再通过 订阅 连接,岂不美哉~

又因为是使用注解的方式,通过继承空的 Observer 接口,再手动订阅,你甚至在任何一个自定义类中就可以收到消息。

三、相关源码

1、RxJava

RxJava 整个框架就是建立在 观察者模式 上,其使用技巧繁多复杂,令人头晕目眩,但是等你剥开它一层又一层的外衣,你会发现,原来你小子身上就这几根毛呢~

点击查看:五分钟速读RxJava源码

2、生命周期组件 Lifecycle

这里留个坑位,以后会写一篇源码分析文章。

四、小结

许许多多的设计模式都被人挖掘出新的使用方式,但不变的仍然是它的原理,我曾说过无数遍,基于设计模式的基本原则,你想怎么玩就怎么玩。

设计模式篇(六)——观察者模式相关推荐

  1. 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

    设计模式 ( 十五 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...

  2. 从王者荣耀看设计模式(六.观察者模式)

    从王者荣耀看设计模式(观察者模式) 一.简介: 王者荣耀是由十位玩家组成的RGB类电子竞技游戏.为了游戏良好的体验感,王者荣耀具有完备的系统提示机制.游戏开始时,系统会发出提示音:"敌军还有 ...

  3. C#设计模式之十六观察者模式(Observer Pattern)【行为型】

    C#设计模式之十六观察者模式(Observer Pattern)[行为型] 原文:C#设计模式之十六观察者模式(Observer Pattern)[行为型] 一.引言 今天是2017年11月份的最后一 ...

  4. 设计模式篇-观察者模式

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接: https://blog.csdn.net/weixin_43212912/art ...

  5. 程序员内功-设计模式篇

    一. 什么是设计模式 纠结了好久,今天终于下定决心开始写设计模式系列,因为这个系列章节确实不好写,在这之前,也看了好多关于设计模式的博客.视频.书籍等,最后结合自己的理解,亲自动手实操代码,完成该章节 ...

  6. amd860k能装黑苹果吗_黑苹果配置 篇六:黑苹果硬件选购指南之终篇--2019年8月

    黑苹果配置 篇六:黑苹果硬件选购指南之终篇--2019年8月 2019-08-17 17:31:38 26点赞 238收藏 33评论 你是AMD Yes党?还是intel和NVIDIA的忠实簇拥呢?最 ...

  7. Java多线程编程实战指南+设计模式篇pdf

    下载地址:网盘下载 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而 解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然 ...

  8. java多线程编程_Java多线程编程实战指南+设计模式篇.pdf

    Java多线程编程实战指南+设计模式篇.pdf 对Java架构技术感兴趣的工程师朋友们可以关注我,转发此文后私信我"Java"获取更多Java编程PDF资料(附送视频精讲) 关注我 ...

  9. 计算机启用时间 查找方式,电脑实用知识技巧 篇六:不需要第三方软件,这种方法查看系统启动时间...

    电脑实用知识技巧 篇六:不需要第三方软件,这种方法查看系统启动时间 2019-04-04 09:19:21 0点赞 0收藏 0评论 上次我们说到:开机小助手,要让我们看到开机时间,必须添加自启动项目, ...

  10. Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛

    Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览. ...

最新文章

  1. Android ViewPager和Fragment实现顶部导航界面滑动效果
  2. 域名系统DNS、文件传送协议FTP、动态主机配置协议DHCP、远程登录协议TELNET、电子邮件协议(SMTP/POP3/IMAP)、常用端口
  3. 编程界的“二向箔”——Dart元编程
  4. MP 启动注入 SQL 原理分析
  5. 阿里云rds for mysql平台介绍_阿里云RDS for MySQL 快速入门——笔记
  6. oracle把两个字段拼接在一起,请问Oracle中两个日期拼接在一起的语句应该怎么写?...
  7. Oracle基础入门完整版(课程笔记)
  8. Windows server 2003设置IP安全策略批处理脚本
  9. 机器学习、深度学习、计算机视觉、自然语言处理及应用案例
  10. L1-064 估值一亿的AI核心代码 (20 分)
  11. 2018俄罗斯世界杯亚洲区12强赛记录
  12. oracle01004,Oracle goldengate的OGG-01004 OGG-1296错误
  13. C++ 使用命令行编译程序
  14. 中文字符点阵信息的显示和插入新字符(基于HZK16 ASC16软字库)
  15. 解决浏览器能上网而其他软件无法联网的问题
  16. MyBatis的参数传递
  17. 图神经网络论文阅读(九) Break the Ceiling: Stronger Multi-scale Deep Graph Convolutional Networks,NeurIPS2019
  18. MS COCO数据集
  19. 中国工程院院士王国法:想实现煤炭完全无人化开采
  20. 骨传导耳机有害处吗?骨传导耳机科普

热门文章

  1. Win10台式电脑怎么不拔网线断网
  2. 开发QQ桌球瞄准器(5):使用注册表保存配置
  3. 度量学习Metric Learning
  4. 操作性定义(Operational Definition)
  5. JAVA除数为0报错?
  6. 亚马逊测评账号关联因素有哪些?
  7. win10 微信/QQ等能听到别人说话,别人听不到自己说话解决方案
  8. 小水智能-智能楼宇智慧建筑3D可视化系统,实现对实时数据的整合处理
  9. 第六课:计算两数的GCF(最大公因数)(基于AndroidStudio3.2)
  10. uva10098--排列