前言
在工作中,我们经常会遇到这样子的需求,对象A需要实时关注对象B内容的变化,然后对象A再根据变化的内容做一些反应。通常我们的做法是A实现个接口回调传给B,当B内容变化时通知给A。但这种方式导致B与A强耦合,后续如果新增一个对象要关注B的内容的就特别的麻烦。
要想解决这个问题,或许有一个模式可以帮到你。不仅允许对象A观察对象B的内容的变化,而且A可以动态调整是否要持续监控B的内容,就算新增一个观察者对象C也可以轻松扩展。这个模式就是我们今天要讲的观察者模式。
观察者模式
定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
上述的定义引用自《Head First设计模式》。上述的定义中表述了观察者模式的两个重要的特点:一对多依赖、观察者的被动更新。
举一个生活中常见的例子:订阅报纸。首先要想时时刻刻都获得最新的报纸,我们需要主动向出版社订阅报纸如:《人民日报》,这时出版社就会把我们的信息加入到他的订阅者信息列表中,当人民日报更新时就会给所有订阅这份报纸的人发新的报纸。如果不想在继续收到报纸了,可以取消订阅的,取消订阅之后出版社就会将你的信息从列表中删除,你也就不会再收到最新的报纸。根据定义以及订阅报纸的例子画出如下对照:
主题(出版社)与观察者(订阅者)定义了一对多的关系。观察者依赖主题,只要主题的的状态发生改变,观察者就会收到主题的更新通知。
结构
有了上述的抽象概念之后,我们再通过类图来大体的了解下-观察者模式。
Observer通过register注册到Subject,如此主题Subject持有各个观察者Observer的对象,在Subject状态改变时调用notify,通过Observer的onChange方法通知到各个观察者。如果某个Observer不再关心Subject的状态可以通过unRegister方法取消注册。
实战
需求:做一个天气信息展示的软件。这个软件具备如下功能:1、展示目前天气数据信息。2、天气数据的统计。接入气象局sdk,气象局的sdk提供了如下三个天气数据:温度,湿度,气压。
利用本文讲的观察者模式来设计该软件的架构:主题气象局sdk,观察者展示天气数据信息与天气数据统计两大功能。看如下类图:
主题WeatherData提供天气数据,StatisticsDisplay和CurrentConditionsDisplay两大天气情况展示功能都通过统一的接口registerObserver注册到主题中。一旦有数据的变化,主题就会调用notifyObserver,遍历目前注册的观察者并调用其update接口。StatisticsDisplay观察者收到数据更新就能够统计出目前的最小平均值和最大观测值。CurrentConditionsDisplay观察者收到数据更新就能够显示当前天气情况。
该设计方法,对扩展也很友好。后续假设要加上一个天气预报的功能,只要实现Observer,DisplayElements两大接口并注册到Subject中即可。
观察者模式使用起来就是这么的简单,下面我们来看看者当中使用到的设计原则。
从模式中窥探设计原则
设计原则:为了交互对象之间的松耦合设计而努力。
观察者模式对该原则进行完美的诠释。对于主题来说,主题只知道观察者实现了某个接口(Observer的接口)。主题不需要知道观察者的具体的实现类是谁,实现类做了什么或者任何细节。后续增加任何新观察者,主题不需要关心,也都能够支持。主题唯一只依赖一个保存观察者的列表,因此后续还能动态的增加观察者,而根本不需要修改到主题的内容。
使用场景
当遇到如下的场景就可以考虑采用观察者模式:
- 需求中有两个独立的个体,一个个体依赖另外一个。将这两个封装在独立的对象中,以使他们相互独立且可互相复用。
- 当一个对象改变时必须要同时通知多个其他对像时,而不必关心对象是谁、对象的数量。
优缺点
优点:
- 观察者与主题之间松耦合:一个主题只知道它有一系列的观察者,每个观察者都符合抽象的Observer接口。并不关心观察者具体是谁,以及是干什么用的。
- 主题支持广播式通信:数据变化,会自动广播给所有已注册的对象。主题并不关心有多少个注册的对象,主题只负责通知所有的已注册的对象。这样就给任意时刻进行增加删除观察者的自由。
缺点: - 意外的更新:一个观察者不知道其他观察者的存在,对改变主题的的代价一无所知。对主题一个看似无害的操作都有可能导致其他观察者出现无法估计的错误。因此这个必须要有一个明确的使用准则,不然很容易引起错误的更新。
总结
观察者模式是一种订阅-广播式的设计模式。定义了对象之间一对多关系。观察者与主题之间是一种松耦合的关系,两者之间的细节对对方都是透明的,只要实现一些固定的接口就可以让整个模式跑起来。在平常码代码过程中要多注意使用场景与优缺点,多多的练习。
个人公众号:基石分享社
欢迎关注我的公众号,该公众号会持续输出Android开发过程中的各种基础知识的文章,共同学习成长。