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

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

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

场景

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

初始方案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. 两个动作(添加,移除)来操作观察者

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

简单应用

看个观察者模式的应用。

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


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

<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>
复制代码

小结

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

下一篇,我们来介绍 发布订阅者模式并比较它和观察者模式的区别。

相关文章
|
4月前
|
存储 负载均衡 Java
Elasticsearch集群面试系列文章一
【9月更文挑战第9天】Elasticsearch(简称ES)是一种基于Lucene构建的分布式搜索和分析引擎,广泛用于全文搜索、结构化搜索、分析以及日志实时分析等场景。
123 7
|
7月前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
208 1
|
6月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
99 1
|
6月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
75 1
|
6月前
|
设计模式 Java
Java面试题:什么是观察者模式以及如何在Java中实现?
Java面试题:什么是观察者模式以及如何在Java中实现?
44 0
|
6月前
|
设计模式 Java
Java面试题:描述观察者模式的工作原理及其在Java中的应用。
Java面试题:描述观察者模式的工作原理及其在Java中的应用。
46 0
|
6月前
|
设计模式 SQL 安全
Java面试题:设计一个线程安全的内存管理器,使用观察者模式来通知所有线程内存使用情况的变化。如何确保在添加和移除内存块时的线程安全?如何确保任务的顺序执行和调度器的线程安全?
Java面试题:设计一个线程安全的内存管理器,使用观察者模式来通知所有线程内存使用情况的变化。如何确保在添加和移除内存块时的线程安全?如何确保任务的顺序执行和调度器的线程安全?
51 0
|
6月前
|
设计模式 存储 缓存
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
50 0
|
8月前
|
机器学习/深度学习 人工智能 安全
常见人力面试题辅助文章(爱好、崇拜者、座右铭、缺点)
常见人力面试题辅助文章(爱好、崇拜者、座右铭、缺点)
82 1
|
8月前
|
消息中间件 设计模式 前端开发
【面试题】说说你对发布订阅、观察者模式的理解?区别?
【面试题】说说你对发布订阅、观察者模式的理解?区别?
117 0