【设计模式】从前端的角度去理解观察者模式
!!最下面有完整代码
设计模式是干嘛的?
设计模式实际上就是给出某种场景下一个”解题思路”.它不是面向于业务的,而是”实现”层面的.其实对于初步接触这个概念的人比较懵逼.我们需要在进一步了解之前明确一个点,不同的设计模式是应对不同场景的.就比如”工厂模式”和”观察者模式”他们是没法比较的,他们一个是用于”创造”,一个是用于”监听”的.所以我们去理解某个设计模式的时候一定要先去了解它是用于哪种场景的,然后在这种场景下它有什么优势.
观察者模式
核心需求
前面有提到,观察者模式是为了”监听”的.也就是说,我需要对多个数据进行监听.同时这些数据可能需要在某条数据更新后进行一系列操作.
需求分析
1.多个数据同时监听一个共同的数据,当这个数据发生变化时,那些监听的对象都得知道.
2.收到通知后执行其更新函数.
需求案例
假如我现在有以下数据
let test1 = {name:'小明',age:18} let test2 = {name:'小红',age:17} let test3 = {name:'小智',age:16}
然后,我
let me ={ name:'shyno', age:18 }
我的需求是:当”me”自我介绍完之后,”小明”、”小红”、”小智”都要自我介绍一下
首先,这些只是原始数据.如果不考虑设计模式,我直接就写一些函数去解析对比,然后打印.不过显然,为了能复用,我需要抽一些函数出来.广义点讲,这些函数就是”设计模式”的一部分,所以没必要纠结”设计模式”在前端的正确意义.
模式分析
首先,我们需要处理每一个观察对象,对它进行包装和绑定.他们都有一个固定函数,这个函数用来处理更新之后的逻辑.类似下面的
let getNewData= function(data){ let newData = {...data} newData.whenUpdate=function(){ console.log(`我的名字是:${this.name},我的年龄是:${this.age}`) } return newData }
可是,有一种更方便的方案,即构造函数.因为你其他的属性我并不关心,只需要加一个whenUpdate函数就够了.那我们把这个构造函数叫做”观察者”,也就是说它一直盯着变化,也只盯着变化
观察者
//观察者 function Observer(){ this.whenUpdate=function(){ // console.log(`我的名字是:${this.name},我的年龄是:${this.age}`) } }
我们可以在whenUpdate里面写上公共逻辑,但是实际上假如大家虽然都需要在更新的时候做一件事,可是不是同一件事,比如test1改变后,需要打印name,test2改变后需要打印age,test3是两个都打印,那很明显,他们的共同点只有一个,即只有whenUpdate.综上所述,观察者只是提供一个名字加whenUpdate的函数.
//观察者 function Observer(){ this.whenUpdate=function(){ } }
然后,再结合需求去具体化观察者.
function ObserverSpecific(obj){ Observer.apply(this) this.name =obj.name this.age =obj.age //重写whenUpdate this.whenUpdate=function(){ console.log(`我的名字是:${this.name},我的年龄是:${this.age}`) } } let Observer_test1 =new ObserverSpecific(test1) let Observer_test2=new ObserverSpecific(test2) let Observer_test3=new ObserverSpecific(test3)
这样我们就得到了三个具体的观察者.
观察者列表
当其中一个变化,大家都有反应,这个”大家”很明显是在一个集合里的.我们通过这个集合去储存和遍历他们.所以我们需要一个列表去存这些具体观察者.同时这个列表本身也应该是可操作的.最基础的增删改查应该是要有的.
//抽象观察者列表 function ObserverSpecificList(){ this.List = [] this.add=function(option){ this.List.push(option) } this.remove=function(option){ this.List=this.List.filter((item)=>item!==option) } this.getOption=function(index){ this.List=this.List.filter((item,i)=>i===index)[0] } this.update=function(data){ } }
再根据观察者去具体化观察者列表,这里不把update写死的原因还是观察者里的那个更新函数不是固定的,我们是whenUpdate,但是可能其他观察者我们定义的”afterUpdate”等函数名,所以我们先抽象化一个观察者列表,再去具体化他.
//具体观察者列表 function ObserverSpecificListSpecific(){ ObserverSpecificList.apply(this) //重写update this.update=function(data){ this.List.forEach(element => { element.whenUpdate(data) }); } }
可观察对象(被观察者)
在这个案例中”me”是被观察者,也就是说大家都等着我呢.那这个”me”有什么特性呢?
首先,”me”是被观察的,那么有哪些观察者观察我呢?所以就需要一个观察者列表,那我同时也能操作这个列表,由此可知.被观察者实际上是继承于观察者列表的.虽然他们看上去很不一样.
//可观察对象 function Observed (obj){ ObserverSpecificListSpecific.apply(this) //被观察者实际上拥有观察者列表的所有属性 this.name =obj.name //同时被观察者有自己特有的属性和函数 this.age =obj.age //重写update this.say=function(data){ console.log(`我的名字是:${this.name},我的年龄是:${this.age}.我自我介绍完了,轮到你们自我介绍了;`) this.update() } }
也就是说,作为可观察对象,他是能通过观察者列表去让所有的观察者的whenUpdate去执行的.那我们从代码流程的角度去分析一下,观察者模式实际上就是,给每个被观察者加上观察者列表相关属性,观察者列表里所有的元素都是观察者,他们都有个函数去执行更新后的操作(whenUpdate),他们把这个操作给了观察者列表.让观察者列表放在一个函数中执行(update).当被观察者执行某个函数之后(say)就可以调用观察者列表的这个函数(update),然后所有观察者的更新后的操作(whenUpdate)也得到了执行.
完整代码
//观察者 function Observer(){ this.whenUpdate=function(){ // console.log(`我的名字是:${this.name},我的年龄是:${this.age}`) } } //抽象观察者列表 function ObserverSpecificList(){ this.List = [] this.add=function(option){ this.List.push(option) } this.remove=function(option){ this.List=this.List.filter((item)=>item!==option) } this.getOption=function(index){ this.List=this.List.filter((item,i)=>i===index)[0] } this.update=function(data){ } } //观察者示例 function ObserverSpecific(obj){ Observer.apply(this) this.name =obj.name this.age =obj.age //重写whenUpdate this.whenUpdate=function(data){ console.log(`我收到了${data}的自我介绍邀请,我的名字是:${this.name},我的年龄是:${this.age}`) } } //具体观察者列表 function ObserverSpecificListSpecific(){ ObserverSpecificList.apply(this) //重写update this.update=function(data){ this.List.forEach(element => { element.whenUpdate(data) }); } } //抽象可观察对象 function Observed (obj){ ObserverSpecificListSpecific.apply(this) //被观察者实际上拥有观察者列表的所有属性 this.say=function(){ } } //可观察对象 function ObservedSpecific (obj){ Observed.apply(this) this.name =obj.name //同时被观察者有自己特有的属性和函数 this.age =obj.age //重写say this.say=function(){ console.log(`我的名字是:${this.name},我的年龄是:${this.age}.我自我介绍完了,轮到你们自我介绍了;`) this.update(this.name) } } /*-------------分割线-------------*/ let test1 = {name:'小明',age:18} let test2 = {name:'小红',age:17} let test3 = {name:'小智',age:16} let me ={ name:'shyno', age:18 } let Observed_me =new ObservedSpecific(me) //将"me"示例化成被观察对象 let Observer_test1 =new ObserverSpecific(test1) //将"test1"示例化成观察者 let Observer_test2=new ObserverSpecific(test2) //将"test2"示例化成观察者 let Observer_test3=new ObserverSpecific(test3) //将"test3"示例化成观察者 Observed_me.add(Observer_test1) //"me"将"test1"放到自己的观察者列表里 Observed_me.add(Observer_test2) //"me"将"test2"放到自己的观察者列表里 Observed_me.add(Observer_test3) //"me"将"test3"放到自己的观察者列表里 // Observed_me.remove(Observer_test1) //"me"将"test1"移出观察者列表 Observed_me.say()
执行结果