【设计模式小书】用最简单的方式聊一下观察者模式

观察者模式,是JavaScript设计模式之一。当然也不仅仅限于JavaScript这门语言,网上对该模式的介绍已是多如牛毛,而且讲得各有特色各有心得。即便如此,笔者仍精心准备了这篇博客,期望用最简单的方式来介绍下该模式。

首先来看下维基百科对 观察者模式 的解释:

观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。

换个角度讲:

观察者模式是一种行为型设计模式,它建立了一个一对多的依赖关系。在这种模式中,一个对象(称为主题或被观察者)维护了一组依赖于它的对象(称为观察者),并在自身状态发生变化时主动通知观察者。这种通知通常通过调用观察者的特定方法来实现。

其实笔者更倾向于它的另一个名字发布/订阅模式(Publish/Subscribe) ,因为更能表达出该模式的核心思路,那就是:发布订阅两个过程。

讲到这可能会给大家一种讲了但是又没讲的感觉,为了更好地理解观察者模式,让我们用一个实际场景来说明它。假设我们要开发一个天气发布网站,而且支持用户订阅天气信息。那么这个场景下,观察者模式可以派上用场。

首先我们先要根据实际场景提炼出两个核心要素:

对象:在这个场景中,我们有两种对象,一是用户(观察者),二是天气数据提供者(主题)。

行为:用户可以订阅城市的天气更新,取消订阅,而天气数据提供者可以发布新的天气信息。

接下来我们就开始用代码一步一步来实现这个需求:

首先我们定义一个天气数据提供者发布者

js 复制代码
class WeatherCenter {
  constructor() {
    this.observers = []; // 观察者列表
    this.weather = null; // 当前天气信息
  }
}

那么一起来按照订阅的过程想象下,发布者具有哪些属性或者方法?

  • 首先,我们订阅网站的天气信息,是不是需要给用户提供订阅入口?那么WeatherCenter中必定会有一个方法提供给我们实现订阅
  • 其次,如果网站要向订阅者们发送天气信息,是不是需要一个方法去做?那么WeatherCenter中必定会有一个方法实现发布
  • 再然后,如例子所说如果我突然不想订阅了,那么WeatherCenter中必定会有一个方法提供给我们实现取消订阅

说到这里一切都了然了,下面还是讲想象到的东西用代码表达出来吧。

js 复制代码
class WeatherCenter {
  constructor() {
    this.observers = []; // 观察者列表
    this.weather = null; // 当前天气信息
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  setWeather(weather) {
    this.weather = weather;
    this.notifyObservers();
  }

  notifyObservers() {
    this.observers.forEach(observer => {
      observer.update(this.weather);
    });
  }
}

在上面的代码中,WeatherCenter 类代表了天气数据提供者,它维护了一个观察者列表和当前的天气信息。它提供了 addObserverremoveObserversetWeathernotifyObservers 方法,用于管理观察者和通知它们。

接下来,我们创建一个观察者对象,以便订阅天气信息。其实这个对象逻辑很简单,就是提供一个方法供WeatherCenter对象来调用并传入一些信息。

js 复制代码
class WeatherObserver {
  constructor(name) {
    this.name = name;
  }

  update(weather) {
    console.log(`${this.name}收到天气更新:${weather}`);
  }
}

到这里,笔者所要表达的内容已全部实现。接下来我们就看下最终的效果吧,全部代码如下:

js 复制代码
class WeatherCenter {
  constructor() {
    this.observers = []; // 观察者列表
    this.weather = null; // 当前天气信息
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  setWeather(weather) {
    this.weather = weather;
    this.notifyObservers();
  }

  notifyObservers() {
    this.observers.forEach(observer => {
      observer.update(this.weather);
    });
  }
}

class WeatherObserver {
  constructor(name) {
    this.name = name;
  }

  update(weather) {
    console.log(`${this.name}收到天气更新:${weather}`);
  }
}

const weatherCenter = new WeatherCenter();

const user1 = new WeatherObserver("用户1");
const user2 = new WeatherObserver("用户2");

weatherCenter.addObserver(user1);
weatherCenter.addObserver(user2);

weatherCenter.setWeather("晴天");
weatherCenter.setWeather("多云");

运行效果如我们所预料的那样:

在这个示例中,我们创建了一个天气数据提供者 WeatherCenter 和两个观察者 user1user2。然后,我们将观察者添加到主题中,并通过调用 setWeather 方法来更新天气信息。

setWeather 方法被调用时,它会通知所有的观察者,并触发它们的 update 方法,从而使观察者知道天气信息的变化。

通过这个简单的示例,您可以看到观察者模式如何在JavaScript中实现,以及如何帮助我们构建松耦合的应用程序组件。观察者模式使得主题和观察者之间的关系更加灵活,有助于构建可扩展的应用程序。

最后我们总结一下观察者模式的特点和使用注意事项:

优点

  1. 松耦合: 观察者模式可以帮助实现松耦合的对象之间的通信。主题对象和观察者对象之间的关系是一对多的,主题不需要知道观察者的具体细节,只需知道它们实现了特定的接口或方法

  2. 可维护性和可扩展性: 由于对象之间的松耦合性,观察者模式使得系统更容易维护和扩展。您可以轻松地添加新的观察者或修改现有的观察者,而不需要修改主题

  3. 通知机制: 观察者模式提供了一种通知机制,当主题的状态发生变化时,所有观察者都会得到通知并及时更新。这在实时性要求较高的应用程序中非常有用。
    缺点

  4. 过多的通知: 当主题频繁地发生变化时,会导致观察者接收大量的通知,这可能会引起性能问题。因此,在某些情况下,需要谨慎使用观察者模式。

  5. 可能引发循环依赖: 如果观察者之间相互依赖,可能导致循环依赖的问题,这会增加代码的复杂性并可能引发不稳定的行为。
    使用注意事项

  6. 选择合适的场景: 观察者模式适用于当一个对象的状态变化需要通知其他多个对象,并且这些对象的具体数量和类型可能会在运行时变化时。因此,在使用观察者模式之前,需要仔细考虑是否有这样的需求。

  7. 避免过多通知: 确保只有重要的状态变化才会通知观察者,以避免性能问题。可以使用延迟通知或者批量通知来优化。

  8. 处理异常: 在观察者模式中,观察者可能会抛出异常,因此需要适当地处理异常,以确保不会影响其他观察者和主题。

  9. 避免循环依赖: 在设计观察者模式时,要确保避免观察者之间的循环依赖,这可能会导致不可预测的问题。

总之,观察者模式是一种强大的设计模式,它在许多情况下都非常有用。然而,在使用时需要权衡其优点和缺点,并根据具体的应用场景来决定是否使用以及如何使用。正确使用观察者模式可以帮助构建松耦合、可维护和可扩展的软件系统

相关推荐
awonw4 分钟前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
老齐谈电商7 分钟前
Electron桌面应用打包现有的vue项目
javascript·vue.js·electron
柏箱1 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
一颗花生米。4 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐014 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19954 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&5 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
一路向前的月光10 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   10 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web10 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery