0
点赞
收藏
分享

微信扫一扫

当面试官问你什么是观察者模式的时候,用这篇文章去回答他!

摘要

本文介绍了观察者模式的定义,作用,和基本实现的要点,希望对你有帮助。

场景

用户点了网页中的按钮,接下要做三件事情(假设三件事没有依赖关系)。

初始方案1:伪代码:

document.getElementById('btn').addEventListener('click', {  
doSomething1()
doSomething2()
doSomething3()
})

复制代码

上面的三个函数表示三件不同的事情。

好了,就从这里出发,我们看看这段代码,然后提一个需求:额外再补充做一件事。

解决:定义一个函数doSomething4,然后添加到click的回调中,如下:

document.getElementById('btn').addEventListener('click', {  
doSomething1()
doSomething2()
doSomething3()
doSomething4() // 这是新添加的
})

复制代码

看起来,需求可以很方便的满足,也没有什么问题。但是,我们直接修改了 原来的初始代码 。

大家会觉得:添加新功能要改原来的代码,不是很正常吗,这些代码是不都是我们自己写的吗,改改有什么关系?

有关系,两个理由:

  1. 理论上:违反了软件设计的开闭原则
  2. 实操中:原来的代码是加密的,或者看起来就非常复杂,或者你在远程指导其他人来完成这个需求,或者不是我们自己写的.....  这些情况都让 直接修改原来代码 变得很困难。

以上是背景,下面引出观察者模式的写法。来!

改进方案:伪代码

document.getElementById('btn').addEventListener('click', doSomething1)
document.getElementById('btn').addEventListener('click', doSomething2)
document.getElementById('btn').addEventListener('click', doSomething3)
复制代码

需求:额外再补充做一件事。不需要改动原来的代码哈,只需要添加一句:

document.getElementById('btn').addEventListener('click', doSomething1)
document.getElementById('btn').addEventListener('click', doSomething2)
document.getElementById('btn').addEventListener('click', doSomething3)
document.getElementById('btn').addEventListener('click', doSomething4) // 额外再补充做一件事
复制代码

现在有没有觉得,改进的方案比初始方案好呢?其实,改进的方案中就使用了观察者模式,这是DOM2级事件机制提供给我们的便利。

观察者模式的定义

观察者模式是23种设计模式中的一种。 设计模式就是解决一类编码问题的最优解,就是套路。这个概念与具体的编程语言关系不大,但是由于各个编程语言的特性不同,同一个设计模式在不同的语言中的实现成本也不同。

观察者模式的英文是Observer,下面来看它的两个经典定义:

《js设计模式》中对Observer的定义:一个对象(称为subject)维持一系列依赖于它(观察者)的对象,将有关状态的任何变更自动通知给它们。

《设计模式:可复用面向对象软件的基础》中对Observer的定义:一个或多个观察者对目标的状态感兴趣,他们通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,就会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标感兴趣时,他们可以简单地将自己从中分离。

注意一下,在各个不同的资料(语言背景)中可能看到的定义表述是不同的,不过,他们有共同点:

  1. 描述一对多的关系。多个观察者对一个目标感兴趣
  2. 目标变化之后,会主动通知观察者(他们分别做出各自的行为)
  3. 观察者是可以任意添加和移除的(对目标不再感兴趣就移除)

我们再来看看前面写的代码,检查是否符合上面的标准:

document.getElementById('bnt').addEventListener('click', doSomething1)
document.getElementById('bnt').addEventListener('click', doSomething2)
document.getElementById('bnt').addEventListener('click', doSomething3)
document.getElementById('bnt').addEventListener('click', doSomething4) // 额外再补充做一件事
复制代码

说明如下:

  • 描述一对多的关系。四个观察者(doSomething1,2,3, 4)对一个目标(按钮是否点击)感兴趣
  • 目标变化之后,会主动通知观察者(按钮点击之后,4个动作各自执行)
  • 观察者是可以任意添加和删除(addEventListener来添加观察者,removeEventListener来删除观察者)

写一个观察者模式

下面,用一段简单的代码来实现观察者模式:

// 大漂亮是女神,有很多的爱慕者
const beauty = {
lover: [], // 容器,保存全部的观察者
notify() {
// 通知全部观察者
this.lover.forEach(fn fn());
},
addLover(fn) {
// 添加观察者
this.lover.push(fn);
},
removeLover(fn) {
// 移除观察者
const idx = this.lover.findIndex(item item === fn);
if (idx != -1) {
this.lover.splice(idx, 1);
}
},
};
const lover1 = () => {
console.log('1号爱慕者');
};
const lover2 = () => {
console.log('2号爱慕者');
};
const lover3 = () => {
console.log('3号爱慕者');
};
beauty.addLover(lover1); // 添加观察者
beauty.addLover(lover2); // 添加观察者
beauty.addLover(lover3); // 添加观察者
beauty.notify(); // 1,2,3号
beauty.removeLover(lover1); // 移除观察者
beauty.notify(); // 2,3号
复制代码

上面代码的核心要点:

  1. 一个容器来保存观察者
  2. 一个动作通知全体观察者
  3. 两个动作(添加,移除)来操作观察者

简单应用

看个观察者模式的应用。

下边的代码实现了一个效果:修改了对象的属性值,在视图上有两个地方变化了。

当面试官问你什么是观察者模式的时候,用这篇文章去回答他!_html

一个值变了,两个地方跟着做出改变。是不是一个使用观察者的好场景呢?以下是实现代码

<body>
<div id="id1"></div>
<div id="id2"></div>
</body>
<script>
function observer(key, _value) {
let obj = {}
let listers = []
function notify() { listers.forEach(fn fn(_value)) }
function addLister(fn) { listers.push(fn) }
Object.defineProperty(obj, key, {
get() {
return _value
},
set(newVal) {
if (newVal !== _value) {
_value = newVal
notify()
}
}
})
return { obj, addLister }
}
const { obj, addLister } = observer('age', 10)
addLister(val { document.getElementById('id1').innerHTML = 'listener1 ' + val })
addLister(val { document.getElementById('id2').innerHTML = 'listener2 ' + 2 * val })
obj.age = 10
</script>
复制代码

小结

本文介绍了观察者模式用来解决的问题、定义,基本实现,最后完成了一个小小的响应式 功能, 如果对你理解观察者模式有帮助,欢迎你转发,关注,小额打赏。

当面试官问你什么是观察者模式的时候,用这篇文章去回答他!_观察者模式_02

举报

相关推荐

什么是Java中的观察者模式?

0 条评论