0%

观察者模式(Observer Pattern)

这是设计模式系列第二篇博文,观察者模式,也是《Head First 设计模式》的学习笔记。

定义

主题(Subject):主题对象管理某些数据。
观察者(Observer):监听主题的数据变化,执行响应的操作。

观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

讨论

在日常开发中,经常用到观察者模式,相信大家都已经对观察者模式烂熟于心。但是是不是真的用的好呢?我提几个问题,看看大家是否有思考过,也欢迎留言讨论。

通知数据更新,是选择以推的方式来实现还是以拉的方式来实现?各有哪些优缺点?

我们先定义下推和拉。
推的方式:通知 Observer 数据有更新时,通知把更新后的数据通过参数一起传递过去;
拉的方式:数据更新时,只通知 Observer 数据有更新,具体需要哪些数据由 Observer 主动调 Subject 的接口查询。

拉的方式,Observer 并不知道具体更新的数据是哪些。收到数据更新时,只能一股脑的把自己需要的数据都重新查询一遍,有时候是很低效的。其次,有时候 Observer 需要知道更新前的数据以及更新后的数据,使用拉的方式也无法解决这种场景。

如果选择推的方式,以上两个缺点都可以解决。但是同时也带来了一个新的问题。在推的方式中,更新的数据是以参数来传递的。当需要新增参数时,就需要修改所有 Observer 的方法,略蛋疼。

但其实也不是没有解决方法,可以使用一个 model 来传递。当通知更新的参数大于三个时,建议以传递 model 的方式来通知更新。

Subject 是否需要支持线程安全?

这个主要还是看使用场景。
往往 Observer 观察到数据变化时,简单计算后刷新 UI。在 iOS 上,必须在主线程中刷新 UI 界面。博主以为,为了保证效率,这种情况可以是线程不安全的,但建议在代码中增加assert,在开发阶段发现和解决多线程使用的问题。

在通知 Observer 过程中,是否允许调用addObserver或者removeObserver来添加或者移除观察者?

Subject 中一般用数组或者集合来保存所有 Observer。在iOS中,在遍历数组的过程中修改数组,会抛异常。
博主建议,addObserver或者removeObserver时,如果发现正在遍历 Observer 通知数据更新,则将 Observer 数组拷贝一份,操作这份拷贝。在通知 Observer 完毕时,再替换为拷贝后的数组。

一般情况下,Subject 的 Observer 不会太多,拷贝 Observer 数组并不会有太大的性能消耗。

设计原则

为了交互对象之间的松耦合设计而努力。

传送门

策略模式(Strategy Pattern)