阅读 208

js设计模式系列篇:观察者模式

定义

观察者(observer)模式是js中应用很广泛的一种设计模式。它主要是通过在一个目标对象上维护一系列依附于它的观察者(observer)对象来实现,在目标对象上有任何状态改变时,会自动地通知给观察者。

先来看一个简单的实现

观察者的实现

class Observer {     constructor () {}     update (context) {         //观察者收到通知后触发的函数         console.log(`recieved data is ${context}`)     } } 复制代码

以上是简单的观察者的定义。目标对象在需要的时候可以触发update函数以实现对观察者的通知。

观察者列表

//观察者列表 class ObserverList {     constructor () {         //用数组去保存观察者         this.list = []     }     //添加一个观察者     add (observer) {         this.list.push(observer)     }     //获取观察者的个数     count () {         return this.list.length     }     //移除观察者     remove (observer) {         this.list = this.list.filter(item => item !== observer)     }     //获取观察者     getIndexAt (index) {         if (index > -1 && index < this.count()) {             return this.list[index]         }     } } 复制代码

以上是观察者列表,list数组中保存的是一个个Observer类的实例,即观察者。我们可以用该类,对依附在某一个目标对象上的观察者进行统一的管理和维护。

目标对象

//目标对象 class Subject {     constructor () {         //观察者依附于目标对象上         this.observers = new ObserverList()     }     //在目标对象上新增一个观察者     addObserver (observer) {         this.observers.add(observer)     }     //从目标对象上移除观察者     removeObserver (observer) {         this.observers.remove(observer)     }     //通知观察者     notify (context) {         const len = this.observers.count()         for (let i = 0; i < len; i++) {             this.observers.getIndexAt(i).update(context)         }     } } 复制代码

从以上代码可以看到,目标对象实例上挂载了一个ObserverList实例对象。用来保存依附在该目标对象上的所有观察者。当目标对象状态有变化时,可以通过notify函数,遍历触发所有观察者上的update函数,以此来实现对所有观察者的通知

代码实现完毕,我们现在来试验一下

//创建观察者 const observer1 = new Observer() const observer2 = new Observer() //创建一个目标对象 const subject = new Subject() //将两个观察者加入到目标对象上的观察者列表中 subject.addObserver(observer1) subject.addObserver(observer2) //通知观察者 subject.notify('hello my observer') 复制代码

运行以上代码,可以在控制台看到输出了两次,如下图

image.png

这就说明目标对象成功通知到了依附在自身的两个观察者

我们来试验一下移除观察者功能

//创建观察者 const observer1 = new Observer() const observer2 = new Observer() //创建一个目标对象 const subject = new Subject() //将两个观察者加入到目标对象上的观察者列表中 subject.addObserver(observer1) subject.addObserver(observer2) //移除观察者 subject.removeObserver(observer1) //通知观察者 subject.notify('hello my observer') 复制代码

以上代码中,目标对象在通知观察者之前移除了其中一个观察者,所以我们可以看到控制台只打印了一次消息

image.png

小案例-画彩虹

在了解完具体的观察者模式之后,我们来实现一个小案例。最终的效果如下图

20211025-165418.gif

实现出来的效果是,按住下方的小黑圈左右拖动,上方的7个彩虹颜色的div的宽度也会相应地放大和缩小。

在本案例中,小黑圈就是目标对象,7个彩虹颜色地div就是依附在该小黑圈中的观察者。当小黑圈的坐标发生变化时,需要通知给这些观察者,告诉他们需要改变自身的宽度。

下面来看具体的实现

html结构

样式部分省略

<div class="rainbow" style="background-color: rgb(255,0,0);"></div> <div class="rainbow" style="background-color: rgb(255,165,0);"></div> <div class="rainbow" style="background-color: rgb(255,255,0);"></div> <div class="rainbow" style="background-color: rgb(0,255,0);"></div> <div class="rainbow" style="background-color: rgb(0,127,255);"></div> <div class="rainbow" style="background-color: rgb(0,0,255);"></div> <div class="rainbow" style="background-color: rgb(139,0,255);"></div> <div id="progress-bar"></div> 复制代码

定义observer

class Observer {     //item是需要充当观察者的7个div元素     constructor (item) {         this.item = item     }     //观察者收到通知时,更新自身的width     update (data) {         this.item.style.width = data + 'px'     } } 复制代码

定义观察者列表

//观察者列表,跟之前实现的差不多 class ObserverList {     constructor () {         this.list = []     }     add (observers) {         this.list.push(observers)     }     count () {         return this.list.length     }     getIndexAt (index) {         return this.list[index]     } } 复制代码

目标对象

//目标对象的实现也基本上一样 class Subject {     constructor (subject) {         this.observers = new ObserverList()     }     addObserver (observers) {         this.observers.add(observers)     }     notify (data) {         const len = this.observers.count()         for (let i = 0; i < len; i++) {             this.observers.getIndexAt(i).update(data)         }     } } 复制代码

之后我们要做的就是,获取7个div元素充当观察者,并添加到我们实例化的目标对象上。如下

const rainbows = document.querySelectorAll('.rainbow') const bar = document.getElementById('progress-bar') const barSubject = new Subject() rainbows.forEach(item => {     const observer = new Observer(item)     barSubject.addObserver(observer) }) 复制代码

在上面的代码中,我们获取了7个div对象,并遍历用每一个div实例化一个Observer,并添加到目标对象上。

之后要做的就是实现目标对象的拖放,并在拖放过程中通知观察者即可,实现拖放的部分省略,这里给出通知给观察者的部分,如下

document.onmousemove = function (event) {     event = event || window.event;     const sl = document.body.scrollLeft || document.documentElement.scrollLeft;     const left = event.clientX - ol;     box.style.left = left + sl + "px";     //通知观察者     barSubject.notify(left + sl) } 复制代码

到此,案例就完成了。希望对大家有所帮助。有不对的地方欢迎大佬们指正。

注意:观察者模式和大家熟知的发布/订阅者模式是有区别的,我会在下一篇文章中介绍


作者:D_L11
链接:https://juejin.cn/post/7022934151665811463


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐