JavaScript设计模式之观察者模式实例详解

JavaScript设计模式之观察者模式实例详解

概述

观察者模式是一种行为型设计模式,它定义对象之间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都将得到通知并自动更新。观察者模式能够使我们建立松散耦合关系,从而提高系统的灵活性和可维护性。

实现

在JavaScript中,观察者模式的实现主要依靠两个对象:被观察的对象和观察者对象。被观察的对象负责维护一个观察者列表,而观察者对象则通过订阅这个列表中的被观察的对象,从而接收这个对象的状态更新通知。

下面是一个简单的实现示例:

class Subject {
  constructor() {
    this.observers = [];
  }

  registerObserver(observer) {
    if (!this.observers.includes(observer)) {
      this.observers.push(observer);
    }
  }

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

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

class Observer {
  constructor(subject, name) {
    this.subject = subject;
    this.name = name;
  }

  update() {
    console.log(`${this.name} received a notification.`);
  }

  subscribe() {
    this.subject.registerObserver(this);
  }

  unsubscribe() {
    this.subject.removeObserver(this);
  }
}

在上面的代码中,Subject类代表被观察的对象,Observer类代表观察者对象。Subject类中定义了observers数组属性,表示观察者列表,以及registerObserverremoveObservernotifyObservers方法,分别用于添加观察者、移除观察者和通知所有观察者。

Observer类中定义了subject属性,表示观察的对象,以及name属性,表示观察者的名字。update方法表示观察者接收到更新通知后的回调函数。subscribeunsubscribe方法分别用于在被观察的对象中添加和移除当前观察者。

下面是一个使用示例:

const subject = new Subject();

const observer1 = new Observer(subject, 'Observer 1');
const observer2 = new Observer(subject, 'Observer 2');

observer1.subscribe();
observer2.subscribe();

subject.notifyObservers();

observer1.unsubscribe();

subject.notifyObservers();

在上面的代码中,首先创建了一个Subject对象,然后创建了两个Observer对象。接着,分别对两个观察者对象调用subscribe方法,将它们添加到被观察的对象中。然后,调用Subject对象的notifyObservers方法,通知所有观察者。最后,移除一个观察者后再次调用notifyObservers方法。

示例说明

快递追踪实例

一个典型的应用场景是快递追踪。假设有一个快递公司管理系统,其中有一个快递追踪模块,用户可以订阅自己的快递进度。当快递状态发生变化时,订阅者将收到通知。

class Express {
  constructor() {
    this.observers = [];
    this.state = 'pending';
  }

  registerObserver(observer) {
    if (!this.observers.includes(observer)) {
      this.observers.push(observer);
    }
  }

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

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

  setState(state) {
    this.state = state;
    this.notifyObservers();
  }
}

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

  update(express) {
    console.log(`${this.name} received a notification: ${express.state}`);
  }

  subscribe(express) {
    this.express = express;
    this.express.registerObserver(this);
  }

  unsubscribe() {
    this.express.removeObserver(this);
  }
}

const express = new Express();

const user1 = new User('User 1');
user1.subscribe(express);

const user2 = new User('User 2');
user2.subscribe(express);

express.setState('shipping');

user1.unsubscribe();

express.setState('delivered');

在上面的代码中,Express类代表快递追踪状态,User类代表订阅者。Express类中新增了setState方法,用于设置快递状态并通知所有订阅者。User类中的update方法接收到快递状态更新通知后,将状态显示为指定的提示信息。subscribeunsubscribe方法分别用于添加和移除当前订阅者。

Web 音乐播放器实例

另一个应用场景是 Web 音乐播放器。假设有一个用户目录页面,其中有一个音乐播放器组件,用户点击页面中的音乐时,在播放器中展示该音乐。

class MediaPlayer {
  play() {}

  pause() {}

  seek() {}

  stop() {}
}

class MusicPlayer extends MediaPlayer {
  constructor() {
    super();
    this.observers = [];
    this.currentMusic = null;
    this.isPlaying = false;
  }

  registerObserver(observer) {
    if (!this.observers.includes(observer)) {
      this.observers.push(observer);
    }
  }

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

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

  play(music) {
    if (this.isPlaying) {
      this.stop();
    }

    console.log(`Now playing ${music.name} by ${music.artist}.`);

    this.currentMusic = music;
    this.isPlaying = true;
    this.notifyObservers();
  }

  pause() {
    console.log(`Paused ${this.currentMusic.name}.`);
    this.isPlaying = false;
    this.notifyObservers();
  }

  seek(time) {
    console.log(`Seeked to ${time} seconds.`);
    this.notifyObservers();
  }

  stop() {
    console.log(`Stopped ${this.currentMusic.name}.`);
    this.currentMusic = null;
    this.isPlaying = false;
    this.notifyObservers();
  }
}

class MusicList {
  constructor(musics) {
    this.musics = musics;
  }

  render() {
    const container = document.querySelector('#music-list');

    this.musics.forEach(music => {
      const button = document.createElement('button');
      button.innerText = music.name;
      button.addEventListener('click', () => this.playMusic(music));
      container.appendChild(button);
    });
  }

  playMusic(music) {
    this.musicPlayer.play(music);
  }
}

class MusicInfo {
  constructor() {
    this.container = document.querySelector('#music-info');
  }

  update(musicPlayer) {
    const music = musicPlayer.currentMusic;
    const isPlaying = musicPlayer.isPlaying;

    if (isPlaying) {
      this.container.innerText = `正在播放 ${music.name} - ${music.artist} [${music.duration}]`;
    } else {
      this.container.innerText = '当前未播放任何音乐';
    }
  }
}

const musics = [
  {
    name: 'Song 1',
    artist: 'Artist 1',
    duration: '3:23',
  },
  {
    name: 'Song 2',
    artist: 'Artist 2',
    duration: '4:12',
  },
  {
    name: 'Song 3',
    artist: 'Artist 3',
    duration: '2:51',
  },
];

const musicPlayer = new MusicPlayer();
const musicList = new MusicList(musics);
musicList.musicPlayer = musicPlayer;
musicList.render();

const musicInfo = new MusicInfo();
musicPlayer.registerObserver(musicInfo);

在上面的代码中,MusicPlayer类代表音乐播放器,MusicList类代表音乐列表,MusicInfo类代表音乐信息展示组件。MusicPlayer类中除了实现playpauseseekstop等基本方法外,还定义了registerObserverremoveObservernotifyObservers方法,用于实现观察者模式。MusicList类中的render方法根据音乐列表渲染出一个按钮列表,并通过playMusic方法调用MusicPlayer对象的play方法。MusicInfo类中的update方法接收到音乐状态更新通知后,将状态显示在 HTML 元素上。

最后,在页面加载完成后,实例化音乐播放器对象、音乐列表对象和音乐信息展示组件对象,并将音乐信息展示组件对象订阅到音乐播放器对象中。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript设计模式之观察者模式实例详解 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • 解决Node.js mysql客户端不支持认证协议引发的问题

    问题描述 在使用 Node.js MySQL 客户端时,可能会遇到以下错误: Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client 这个错误发…

    node js 2023年6月8日
    00
  • npm install编译时报”Cannot read properties of null (reading ‘pickAlgorithm‘)”错误的解决办法

    首先,需要明确的是,这个错误通常是由于安装或更新依赖时出现问题导致的。下面是该错误的完整解决办法: 1. 升级npm和Node.js 首先,应该确保您正在使用最新版本的npm和Node.js。您可以通过运行以下命令来检查它们的版本: npm -v node -v 如果您没有安装最新版本,则应该通过Node.js官方网站下载安装最新版本的Node.js,npm…

    node js 2023年6月8日
    00
  • 解决运行vue项目内存溢出问题

    解决 Vue 项目内存溢出问题需要从多个方面入手,下面是一些常见的解决办法: 1. 尽可能避免大对象的创建 在 Vue 的项目中,有时候我们需要创建大对象,这些大对象会占用大量的内存空间,导致内存溢出。因此,我们需要尽可能地避免大对象的创建。 比如,我们可以避免创建过大的数组或者对象,这样可以减少内存的占用。还可以使用函数式编程中的惰性计算,避免不必要的数据…

    node js 2023年6月8日
    00
  • node.js中的fs.mkdir方法使用说明

    当需要在Node.js中创建一个新的文件夹时,可以使用fs.mkdir()方法。下面是该方法的使用说明: fs.mkdir() 这个方法用于在文件系统中创建一个新的目录。它可以接受以下参数: 语法 fs.mkdir(path[, options], callback) 参数 path (string):创建目录的完整路径 options (Object) 可…

    node js 2023年6月8日
    00
  • 详解Node.JS模块 process

    详解Node.JS模块 process Node.JS提供了一个全局模块process,它提供了与当前进程的交互能力。在本文中,我们会详细介绍process模块的各种用法。 获取启动NodeJS应用程序的命令行参数 process模块的argv属性返回一个数组,该数组包含了NodeJS应用程序启动时传递给程序的命令行参数。 // demo1.js conso…

    node js 2023年6月8日
    00
  • 深入浅出了解Node.js Streams

    针对“深入浅出了解Node.js Streams”的完整攻略,我这里给出了以下的讲解过程: 1. 什么是Node.js Streams? 在Node.js中,Streams是一种处理流数据的抽象接口,它允许我们通过交叉逐步把数据片段以一定的速率传递到处理器中,同时避免了在一开始就将整个数据块读取到内存中,这也是 Streams 所提倡的“逐块读取、逐块处理”…

    node js 2023年6月8日
    00
  • JavaScript运行机制之事件循环(Event Loop)详解

    JavaScript运行机制之事件循环(Event Loop)详解 前言 JavaScript是一门具有单线程执行机制的脚本语言,这意味着它一次只能执行一个任务,不能同时进行多个任务的处理。然而,在进行异步编程时,为了避免出现阻塞,我们经常会使用回调函数。那么,浏览器是如何处理这些异步任务的呢?答案是事件循环机制。 什么是事件循环? 事件循环(Event L…

    node js 2023年6月8日
    00
  • Nodejs下DNS缓存问题浅析

    Nodejs下DNS缓存问题浅析 当我们使用Nodejs时,偶尔会遇到DNS解析出现问题的情况,这可能是由于DNS缓存导致的。这篇文章将探讨如何在Nodejs中解决DNS缓存问题以及如何刷新DNS缓存。 DNS缓存问题 当我们使用Nodejs创建一个HTTP请求时,Node会优先使用本地DNS缓存来解析目标主机名以获取其IP地址。如果DNS缓存中没有找到,N…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部