这是设计模式系列第二篇博文,观察者模式,也是《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 数组并不会有太大的性能消耗。
设计原则
为了交互对象之间的松耦合设计而努力。