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

yizhihongxing

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日

相关文章

  • KnockoutJS 3.X API 第四章之数据控制流foreach绑定

    KnockoutJS是一个JavaScript库,可以轻松地将数据绑定到HTML页面中。KnockoutJS的核心功能是数据绑定,而其中一个重要的数据绑定功能是“foreach”绑定。本文主要详细讲解KnockoutJS 3.X API 第四章之数据控制流foreach绑定的使用方法。 1. foreach绑定概述 “foreach”绑定可用于循环渲染一组数…

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

    Node.js中的fs模块提供了很多文件系统相关的功能,其中包括更改文件所有者的方法lchown。本文将详细解释如何使用fs.lchown方法。 fs.lchown方法的用途 fs.lchown方法用于更改文件或目录的所有者。不同于fs.chown方法,它不会跟踪链接,并且仅在操作系统支持它时才有用。 fs.lchown方法的语法 fs.lchown(pat…

    node js 2023年6月8日
    00
  • 详解nodejs 文本操作模块-fs模块(四)

    详解nodejs 文本操作模块-fs模块(四) 在 nodejs 中,fs 模块是处理文件和目录的核心模块。在读取或写入文本数据时,fs 模块提供了多种方法和选项。本文将详细讲解如何使用 fs 模块进行文本操作。 读取文本文件 使用 fs.readFile() 方法可以读取文本文件。该方法包含三个参数:文件路径、编码格式和回调函数。例如,下面的示例将读取指定…

    node js 2023年6月8日
    00
  • Node输出日志的正确方法示例

    下面是Node输出日志的正确方法示例完整攻略。 标准输出和错误输出 在Node中输出日志有两种方式:标准输出和错误输出。标准输出是指程序运行时输出的一般信息,而错误输出是指程序运行时产生的错误信息。两者都可以用Node的console对象进行输出,具体方法如下: // 标准输出 console.log(‘This is a log message.’); /…

    node js 2023年6月8日
    00
  • 基于JavaScript实现一个简单的Vue

    下面我将为你详细讲解“基于JavaScript实现一个简单的Vue”的完整攻略。 什么是Vue Vue是一个渐进式的JavaScript框架,它被设计用于构建大型单页应用(SPA)。Vue提供组件化的开发模式,使得代码结构更加清晰易懂,提高开发效率,降低维护成本。 Vue的核心概念 在我们开始实现一个简单的Vue之前,先让我们了解一下Vue的核心概念: 数据…

    node js 2023年6月8日
    00
  • Nest.js中使用HTTP五种数据传输方式小结

    下面我将为你详细讲解“Nest.js中使用HTTP五种数据传输方式小结”的完整攻略。本文将介绍Nest.js中常用的五种HTTP数据传输方式,即GET、POST、PUT、DELETE和PATCH。 1. GET 在Nest.js中使用GET方式,可以通过@Get()注解实现。例如,以下代码演示了如何使用GET方法获取“/hello”路由的数据: @Get(‘…

    node js 2023年6月8日
    00
  • AJAX实现JSON与XML数据交换方法详解

    AJAX实现JSON与XML数据交换方法详解 什么是AJAX AJAX是一种可以在不重新加载整个页面的情况下更新部分网页的技术。通过AJAX,我们可以在后台向服务器发送和请求数据,然后使用JavaScript在页面上动态加载这些数据,而无需重新加载整个页面。这使得我们可以增强用户交互性并提高Web应用程序的性能。 AJAX如何实现JSON与XML数据交换 A…

    node js 2023年6月8日
    00
  • node.js的http.createServer过程深入解析

    现在我将详细讲解一下“node.js的http.createServer过程深入解析”的完整攻略,希望对您有所帮助。 http.createServer的作用 在深入了解http.createServer的过程之前,我们需要先了解它的作用。http.createServer是node.js中的一个方法,用于创建一个http服务器。我们可以通过该服务器监听客户…

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