程序员

观察者模式

2018-04-08  本文已影响34人  Cstars

观察者模式应该对大多数人都不陌生,iOS中的Notification,KVO都是观察者模式的践行者,甚至GitHub上人气很高的RxSwift,ReactiveCocoa都是对观察者模式的抽象封装。深入了解观察者模式对理解这些框架原理也有裨益。

本篇内容分三部分

一. 概念

二. 特点

三. 实践案例(实现一个监听TextField文字输入事件的阈值化事件流,仿RxSwift throttle操作。)


一.概念

观察者模式定义:

定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知。

基础概念:

在观察者模式中,拥有数据的一方称为主题(subject),依赖数据的一方称为观察者(observer),主题的数据发生变化会通知观察者。

Subject 会实现对应的subject接口,这个接口一般会包含三个方法:addObserver, removeObserver, notifyObserver,分别来用添加、删除观察者以及通知观察者。

Observer 会实现对应的observer接口,这个接口一般会有一个方法用来让Subject调用从而得知变化情况,比如update方法。

观察者模式提供了一种设计,让主题(subject)与观察者(observer)之间松耦合,使对象之间的依赖降到了最低,能让我们建立出更富有弹性的OO系统。主题只关注到观察者实现了某个接口,对具体实现全然不知。主题内部保存有一个观察者的列表,每当发生改变就会逐一通知列表内的观察者。

二.特点

主题(Subject)通知观察者(Observer)有两种方式:

1. push

由Subject主动将数据推给Observer.

例子:update(x: Int, y: String),由于确定了signature,当需要再多传一个参数时,就需要修改很多地方

缺点:Observer被动接收数据,Subject不可能事先预知所有的Observer需要哪些状态,因此Observer可能会受到许多它根本不需要的数据。若把每个变化的状态都推给Observer,显然会出现前面的问题,若不全推,则未来发生需求变化,需要修改很多地方,不够灵活。不过当场景简单的情况下,比如只关注一个状态,此时就很合适,Rx类框架就是采用这种方式。

2. pull

由 Subject 通知Observer状态发生改变(并未直接给Observer数据),observer主动调用Subject 相关get方法获取想要的状态。

例子:update(subject: Subject)或update() ,Observer只需要知道变化已发生,然后直接使用引用的Subject实例的get方法获取想要的属性,即使现在需要多传递参数,只需要给Subject添加一个属性与get方法。

优点:比push方式更灵活,Observer可以仅仅获取自己想要的数据。可以灵活应对以后发生的业务扩展。

三.实践案例

有以下需求:

根据UITextfield中输入的文字搜索结果,为了避免用户快速输入时频繁地发出请求与取消请求,造成客户端与服务器端资源浪费,我们要对触发请求的事件流阈值化,仅当在0.3秒内没有新的触发事件才会将此触发事件通过,否则将重新计时。

最后效果是这样:

注册一个closures并返回一个token用于解除监听

下面分四个步骤完成:

1.首先定义Subject, Observer接口

这里用了泛型是为了表示这个接口不止可以使用String,虽然这个案例只用到了String类型。

2. 创建可监听的事件流类,实现Subject接口(MyObservable协议)

下面方法中的counter是类内部的一个自增属性,每当有新Observer,就会用counter的值给它打一个ID,用于移除Observer时定位。类中使用一个Array属性保存了注册的closures。

下面看看里面有哪些属性:

countDown属性用来阈值化事件流,searchStr用来保存键入搜索框的字符串。每当有新的输入将导致原来的计时器重置,若超过0.3秒没有新输入,则会push给所有Observer当前的searchStr,从而触发一次搜索请求。

这里为了便于测试,将阈值设为1s

3. 封装TextField输入事件

至此,已经基本完成,没有说的就差CancelToken了,实现很简单:

4.使用

从控制台打印可以看出,两个observer都成功接收到了事件。当连续快速输入时,中间的输入事件被忽略了。

上一篇下一篇

猜你喜欢

热点阅读