JS设计模式——观察者模式

什么是观察者模式?

一个或多个观察者对目标的状态感兴趣,它们通过将自己依附在目标对象上以便注册所感兴趣的内容。
目标状态发生改变并且观察者可能对这些改变感兴趣,就会发送一个通知消息,调用每个观察这的更新方法。
当观察者不再对目标状态感兴趣时,他们可以简单地将自己从中分离。

看完后你会觉得这是什么破玩意?不要急,我们举个现实中的例子,来按段落顺序逐步分解下上面这段话的意思。

去饭馆吃饭,点完餐会给你一个号码,然后就和其余点完餐的人一样,坐着等叫号。

每当做好一份餐,服务员就会喊多少号的餐好了。然后每个人都会收到这个消息,并开始检查自己手里的号,看是不是自己的餐好了,如果是自己的餐,就不要坐着了,赶紧去吃饭了。

有些人等着等着,觉得不想吃了,可能是嫌做饭太慢气饱了,就离开不吃了。

抽象组件

我们把上面这个场景再简化抽象成 JS 描述。

比如,一个 JS 对象 O 被修改了,那么它就需要自动通知那些依赖它的对象。这里所有依赖对象 O 的对象都是观察者,而对象 O 就是一个目标。
在这里插入图片描述
根据这个图,我们就可以假设对象 O 都是通过目标类(Subject Class)实例化出来的,而依赖对象 O 的那些对象都是通过观察者类(Observer Class)实例化出来的。

那么我们就可以抽象出来 4 个组件:

  • 目标(Subject)
    维护一系列的观察者,方便添加或删除观察者。对应一个目标类,提供一些注册和通知观察者的接口。

  • 观察者(Observer)
    在目标状态发生改变时,为需要得到通知的对象提供一个更新接口。对应一个观察者类,提供一个更新观察者状态的接口。

  • 具体目标(ConcreteSubject)
    状态发生改变时,向 Observer 发出通知,存储 ConcreteObserver 的状态。对应一个目标实例。

  • 具体观察者(ConcreteObserver)
    存储一个指向 ConcreteSubject 的引用,实现 Observer 类的更新接口,以是自身状态与目标状态保持一致。对应一个观察者实例。

实现

1、为了管理观察者,我们先实现一个 ObserverList 类。也就是一个数组结构,并提供一些常用的数组操作接口。

class ObserverList {
	constructor() {
		this.observerList = []
	}
	add(obj) {
		return this.observerList.push(obj)
	}
	get(index) {
		if (index > -1 && index < this.observerList.length) {
			return this.observerList[index]
		}
	}
	count() {
		return this.observerList.length
	}
	remove(index) {
		this.observerList.splice(index, 1)
	}
	indexOf(obj) {
		return this.observerList.indexOf(obj)
	}
}

2、目标类。提供注册和通知观察者等接口。

class Subject {
	constructor() {
		this.observers = new ObserverList()
	}
	addObserver(observer) {
		this.observers.add(observer)
	}
	removeObserver(observer) {
		let index = this.observers.indexOf(observer)
		if (index >= 0) {
			this.observers.remove(index)
		}
	}
	notify(context) {
		const observerCount = this.observers.count()
		for (let i = 0; i < observerCount; i++) {
			this.observers.get(i).update(context)
		}
	}
}

3、Observer 类。提供一个观察者更新接口。

class Observer {
	update() {
		// ...
	}
}

以上便是观察者模式的实现方式。

使用

下面我们来看下观察模式的使用,我们继续前面饭馆吃饭的场景。

<button id="callClient">Call</button>
// 先实例化一个饭馆
const restaurant = new Subject()

// 实例化两个顾客 a 和 b
const a = new Observer()
const b = new Observer()

// 定义当前被叫号的顾客
let currentObserver

/**
* @desc a 顾客实现自己的更新方法
* @params restaurant 饭堂实例
*/ 
a.update = function(restaurant) {
	// 判断当前的叫号是不是自己
    if (restaurant.observers.get(0) == a) {
        console.log('我是a,我的饭好了')
        // 如果是自己,把当前叫号的顾客赋值成自己
        currentObserver = a
    }
}

// a 顾客实现自己的更新方法
b.update = function(restaurant) {
	// 判断当前的叫号是不是自己
    if (restaurant.observers.get(0) == b) {
        console.log('我是b,我的饭好了')
        // 如果是自己,把当前叫号的顾客赋值成自己
        currentObserver = b
    }
}

// 两位顾客先后在饭堂点餐,把自己注册为观察者
restaurant.addObserver(a)
restaurant.addObserver(b)

// 绑定通知,每次点击通知所有顾客,餐好了
callClient.onclick = function() {
	// 饭好了,通知所有顾客
    restaurant.notify(restaurant)
    // 把拿走饭的顾客,移除,不需要关心了
    restaurant.removeObserver(currentObserver)
}

以上就是一个模拟场景,应用了观察者模式。如果有不正确的地方,望指出。

猜你喜欢

转载自blog.csdn.net/userkang/article/details/86717726
今日推荐