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
数组属性,表示观察者列表,以及registerObserver
、removeObserver
和notifyObservers
方法,分别用于添加观察者、移除观察者和通知所有观察者。
Observer
类中定义了subject
属性,表示观察的对象,以及name
属性,表示观察者的名字。update
方法表示观察者接收到更新通知后的回调函数。subscribe
和unsubscribe
方法分别用于在被观察的对象中添加和移除当前观察者。
下面是一个使用示例:
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
方法接收到快递状态更新通知后,将状态显示为指定的提示信息。subscribe
和unsubscribe
方法分别用于添加和移除当前订阅者。
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
类中除了实现play
、pause
、seek
和stop
等基本方法外,还定义了registerObserver
、removeObserver
和notifyObservers
方法,用于实现观察者模式。MusicList
类中的render
方法根据音乐列表渲染出一个按钮列表,并通过playMusic
方法调用MusicPlayer
对象的play
方法。MusicInfo
类中的update
方法接收到音乐状态更新通知后,将状态显示在 HTML 元素上。
最后,在页面加载完成后,实例化音乐播放器对象、音乐列表对象和音乐信息展示组件对象,并将音乐信息展示组件对象订阅到音乐播放器对象中。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript设计模式之观察者模式实例详解 - Python技术站