javaScript中反应性行为的观察者和公共式图案
#javascript #网络开发人员 #发展 #designpatterns

这些绝对是我最喜欢的两个,最容易理解JavaScript中的设计模式,在这里我将与您分享它们的工作方式以及如何在我们的代码中最好地使用它们。

设计模式是软件开发中常见问题的典型概念解决方案,甚至在JavaScript发明之前就已经想到了大多数问题。因此,在本文中,我将向您展示如何在JavaScript中使用观察者和酒吧设计模式,但请记住,您也可以在许多其他编程语言中使用它们。

总而言之,观察者和酒吧式模式在概念中不仅非常相似,而且在实施中也非常相似。两者都用于允许组件或服务之间的异步通信,并在执行任何其他事件时触发更新。为了更好地理解,请想象一下,每当一个国家通过单击按钮进行更改时,必须通知和更新应用程序的某些部分,类似于React的作用(当然,在非常浅的例子中!)。

摘要:

让我们深入!

观察者模式

观察者模式是一种设计模式,当公共状态发生变化时,它会通知多个观察者(侦听器)。因此,它在对象之间建立了一对一的关系,而不会使对象紧密耦合。

我们可以将这种模式的解释分解为三个不同的部分:

  • 主题:观察者是经理。它维护听众列表,并通过使用添加,删除和通知观察者之类的方法将每个状态变更通知他们。
  • 观察者:是一系列听众。
  • notifier :是一种方法,执行后,将运行每个观察者将更新状态传递给它们。

例子:

想象一个接口,登录时,用户在页面的某些区域中看到了他们的名字。因此,我们需要观察者(侦听器)在登录用户时能够被通知,以用户的名称为状态,然后通过显示用户名来更新页面的所需区域。

/**
 * ./observers.js
 **/

// Array of observers
let observers = [];
// Method to add functions to observers array
export const addObserver = (observer) => {
  observers.push(observer);
};
// Method to remove a function from observers array
export const removeObserver = (observer) => {
  observers = observers.filter((obs) => obs !== observer);
};
// Method to run each of the observers passing the data to them
export const notifyObservers = (data) => {
  observers.forEach((observer) => observer(data));
};
/**
 * ./index.js
 **/

import { addObserver, notifyObservers } from './observers';
// First observer function
const updateSidebarUsername = (data) => {
  console.log(`Sidebar user name: ${data}`);
};
// Second observer function
const updateHeaderUsername = (data) => {
  console.log(`Header user name: ${data}`);
};
// Adding functions to observers
addObserver(updateSidebarUsername);
addObserver(updateHeaderUsername);
// Notifying the observers by passing the data to them
const btn = document.querySelector('button');
btn.addEventListener('click', () => notifyObservers('Diona'));

Play with this code on codesandbox.

代码说明:

  1. 使用模块功能隔离文件中的功能,我们首先在./observers.js中创建主题。它包含观察者列表,添加和删除这些观察者的方法,以及一个称为notifyObservers()的函数,可以通过将更新的状态传递给他们。

  2. 然后,在./index.js文件中,我们有两个功能来说明侦听器(updateSidebarUsername()updateHeaderUsername()),负责在两个称为“侧栏”和“标头”的不同领域更新用户名。然后,我们使用addObserver()方法将这两个听众添加到观察者列表中。

  3. 接下来,我们有一个按钮来说明登录功能,当单击时将执行notifyObservers()方法将用户名作为一个参数,然后将来自观察者列表的两个侦听器拨打两个侦听器并显示两个console.log()

我希望这个简短而简单的示例可以帮助您了解此设计模式的功能,从而使代码在对象之间不紧密耦合而使代码具有反应性。这两个观察者彼此不认识,但是每次通过接收相同状态调用通知者时,它们都可以在某些事件发生时使用该状态来更新应用程序的不同区域。

在真实的应用程序中,可以在必要时使用该方法removeObserver()删除听众以避免性能问题。

酒吧式模式

发布者 - subscriber(pub-sub)模式是messaging patterns的一部分,其描述与观察者一个非常相似

启用申请,向多个感兴趣的消费者宣布事件,而无需将发件人耦合到接收器。

主要差异不仅依赖于描述它的术语,还因为 pub-sub模式是一种多对多的关系。在这里,我们可以让多个发布者在不相互了解的情况下向多个订户发送不同的消息(数据),而在观察者模式中,我们只有一个状态被发送给所有听众(一对多关系)主题本身直接控制所有这些过程。

我们还可以将这种模式的解释分解为三个不同的部分:

  • 消息经纪:出版商和订户之间的桥梁。它包含订阅和退订侦听器所需的所有方法,还包含发布消息。
  • 订户:按相似性分组的订户列表(听众),因为我们可以拥有多种类型的订户。因此,基本上,它将是一个对象,每个道具(某些开发人员称其为频道或主题)将是一个包含相关听众列表的数组。
  • 出版商:一种将使用道具名称和数据的方法,因此它可以根据道具名称找到订户列表,然后迭代将数据传递给每个订户。

Pub-Sub模式是一个很好的解决方案,可用于更复杂和分布式架构,为大量不同客户端持续存在消息,使不同的应用程序从中消耗数据。

其使用的一些示例是:

  • 实时通信:例如,金融公司可以使用它将实时数据分配给订阅的交易者平台。
  • 分布式缓存:可以在多个位置将缓存异步种植以减少流量并最大程度地减少核心服务的负载。
  • 多个数据源:Pub-Sub模式能够从各种来源收集数据,处理并在实时仪表板中显示。

例子:

/**
 * ./pub-sub.js
 **/

// Object to store all subscribers
const subscribers = {};
// Method to subsribe functions by grouping them per eventName
export const subscribe = (eventName, callback) => {
  if (!subscribers[eventName]) {
    subscribers[eventName] = [];
  }
  subscribers[eventName].push(callback);
};
// Method to unsubscribe functions
export const unsubscribe = (eventName, callback) => {
  if (subscribers[eventName]) {
    subscribers[eventName] = subscribers[eventName].filter(
      (subscriber) => subscriber !== callback
    );
  }
};
// Method to publish data to subscribers
export const publish = (eventName, data) => {
  if (subscribers[eventName]) {
    subscribers[eventName].forEach((subscriber) => subscriber(data));
  }
};
/**
 * ./index.js
 **/

import { subscribe, publish } from './pub-sub';
// Event names for listeners grouping
const USER_EVENT_NAME = 'user-event-name';
const  = 'preferences-event-name';
// First user event subscriber function
const updateSidebarUsername = (data) => {
  console.log(`Sidebar user name: ${data}`);
};
// Second user event subscriber function
const updateHeaderUsername = (data) => {
  console.log(`Header user name: ${data}`);
};
// Subscribing user functions by grouping them together using the same event name
subscribe(USER_EVENT_NAME, updateSidebarUsername);
subscribe(USER_EVENT_NAME, updateHeaderUsername);
// Preferences event subscriber function
const updatePreferences = (data) => {
  console.table(data);
};
// Subscribing preferences function
subscribe(PREFERENCES_EVENT_NAME, updatePreferences);
// Publishing data to subscribers
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  publish(USER_EVENT_NAME, 'Diona');
  publish(PREFERENCES_EVENT_NAME, {
    themeColor: 'dark',
    currency: 'EUR',
  });
});

Play with this code on codesandbox.

代码说明:

1-使用模块功能来隔离文件中的功能,我们首先在./pub-sub.js中创建消息代理。它包含一个对象,该对象将使用附加在对象支柱上的数组分组订户(侦听器)。它还包含添加和删除这些订户的方法,以及一个称为publish()的函数,可以通过将一些数据传递给它们来运行这些分组的订户。

2-然后在./index.js文件中,我们有两个const存储事件名称(USER_EVENT_NAMEPREFERENCES_EVENT_NAME) - 将用于对订户进行分组 - 以及三个函数来说明这些订户:updateSidebarUsername()updateHeaderUsername()将使用该函数进行分组相同的事件名称可以一起运行,因为他们负责在两个不同的区域更新用户名,称为“侧栏和标头”。和updatePreferences()将附加到其他事件名称,并将负责更新用户首选项。

3-然后,我们使用subscribe()方法将这三个侦听器添加到订户对象,该方法将接收两个参数:自定义的Prop名称和订户函数。

4-接下来是一个按钮来说明事件操作,当单击将执行两个publish()方法时,第一个将执行与USER_EVENT_NAME属性传递用户名相关的订户,而另一个将在PREFERENCES_EVENT_NAME通过下执行订阅包含用户偏好的对象。

观察者和酒吧模式之间的主要差异

Illustration on how Pub-Sub and Observer Patterns work.

用来形容它们的术语

  • 观察者模式具有诸如主题,观察者,通知者之类的术语。
  • Pub-Sub模式:在这里我们看到消息经纪,订户和出版商。

它们的对象之间的关系

  • 观察者模式:是一种一对多的关系,这意味着主题保持观察者列表,每次国家都会改变时都会被调用。
  • Pub-sub模式:是多对多的,多个发布者可以通过消息经纪人向多个订户发送不同的消息。

听众和数据的控制

  • 观察者模式:受试者知道其观察者,并且知道他们需要如何接收更新的状态以及何时需要调用(在每个状态上更改)。
  • pub-sub模式:这里出版商对他们的订阅者一无所知,这两个组可以存在并且彼此之间的功能。消息经纪通过桥接此差距来起作用。

什么时候使用它们

  • 观察者模式:它的共同用途是应在不同但相关的听众之间共享状态时。
  • Pub-Sub模式:我们在发布者和订户之间没有关系时使用它,因此我们可能会在它们之间发送不同的数据(消息)。

观察者和酒吧模式的一些陷阱

这两种模式很棒,但是您应该知道它们在JavaScript中可能引起的一些陷阱:

  • 内存泄漏:当不再需要避免内存泄漏时,应始终将观察者和订户从主题和消息代理中删除。这是删除observer()和退订()方法的重要性。
  • Infinity Loop :当两个或多个对象相互依赖时发生时,将在一个更新一个时,将调用另一个对象,将调用前一个,依此类推。它也可以称为循环依赖项,您需要意识到它,因为它可能会崩溃。
  • 绩效问题:如果该主题和/或同一出版商的许多订户中有许多观察者,则性能会受到负面影响。避免这种情况的一种方法是限制听众的数量并优化代码来处理它。

参考

结论

,如您所见,观察者和酒吧设计模式以类似的方式工作,提供了一种方法,以使听众在执行某些事件时会被通知,并在应用程序中触发更新(或在不同的关于Pub-Sub模式,不同的客户)。但是,您还看到,在与其他物体建立关系时,它们可能会有所不同:观察者模式是一对一的关系,并且对听众有更多的控制权,而Pub-Sub很多 - to-many,其发布者和订户之间没有界限。

在某些情况下,由于它们的相似性,开发人员倾向于使用相同的名称称呼它们,有时为观察者模式,有时为酒吧式图案,但请记住,它们可以与众不同,如您所见本文。

希望您学会了如何使用它们,它们的主要区别和陷阱。

下次见! ð