在这篇文章中,我们将学习观察者设计模式的使用方法。
观察者设计模式的定义
根据 GoF (设计模式四巨头),观察者模式在对象之间定义了一对多的依赖关系,因此当一个对象改变状态时,其所有的依赖对象都会被通知并自动更新。它也被称为发布-订阅模式。
简单来讲,当你想知道另一个对象的变化时,你可以使用观察者设计模式。
观察者设计模式是一种行为模式,它意味它是一种对象之间的通信模式。
这种设计模式的其他名称是: 事件分发订阅, 监听器模式, 发布订阅模式。
这类设计模式在现实生活中有那些案例?
社交应用是这种设计模式的现实世界的例子。当我们在Instagram上关注某人时,我们只是想知道这个人的状态变化。
如果你正在使用NodeJS或Javascript,那你应该熟悉事件监听器。我们监听一个对象,然后根据对象的变化做出反应。例如,当输入值发生变化时,我们想再次计算一些东西。这通常与我们想通过使用观察者设计模式来做的事情非常相似。
设计模式 UML
让我们看一下UML,之后,我们将使用typescript实现一个简单的例子。
Subject 是我们想要了解的东西。它包含了一些方法,我们可能在不同的文章中看到过不同的名字。
- registerObserver = register = attach = subscribe:此方法用于添加一个观察者。
- unregisterObserver = unregister = detach = unsubscribe: 此方法用于移除观察者。
- notifyObserver = notify: 此方法将在每个观察者之上运行一个方法,这个方法将通知观察者有什么变化。
- update: 当 Subject 改变的时候,我们会运行这个方法。
廉价的谈话!
让我们看看如何用Typescript实现这一点。
首先,我们应该安装Typescript,将我们的代码转译成JavaScript。
npm init -y
npm install typescript
让我们来创建typescript文件:
interface ISubject {
subscribe(observer: Observer):void
unsubscribe(observer: Observer):void
notify():void
}
interface IObserver {
update():void
}
class Subject implements ISubject {
private observers:Observer[] = [];
subscribe(observer:Observer) {
this.observers.push(observer)
}
unsubscribe(observer:Observer) {
this.observers = this.observers.filter((element)=>{
return observer.id === element.id
})
}
notify() {
this.observers.forEach(observer => {
observer.update();
})
}
}
class Observer implements IObserver {
constructor(public readonly id:number) {}
update() {
console.log(`Observer ${this.id} is updating...`)
}
}
const firstObserver = new Observer(1);
const secondObserver = new Observer(2);
const subject = new Subject();
subject.subscribe(firstObserver);
subject.notify();
console.log("-----")
subject.subscribe(secondObserver);
subject.notify();
console.log("-----")
subject.unsubscribe(secondObserver);
subject.notify();
实现之后,我们在第35行和第36行创建了两个观察者。在第38行,我们创建了一个 subject。
在第40行,firstObserver 订阅了这个主题。所以如果我们运行 notify,我们应该只看到 firstObserver 的日志。
在第42行,我们订阅了 secondObserver,所以如果现在运行 notify,我们应该看到这两个观察员的日志。
在第46行,我们取消了对 secondObserver 的订阅,所以如果执行 notify,我们应该只看到 firstObserver 的日志。 最后,日志应该是这样的:
[LOG]: "Observer 1 is updating..."
[LOG]: "-----"
[LOG]: "Observer 1 is updating..."
[LOG]: "Observer 2 is updating..."
[LOG]: "-----"
[LOG]: "Observer 2 is updating..."
让我们实施一些更有意义的东西吧!
interface ISubject {
subscribe(observer: Observer):void
unsubscribe(observer: Observer):void
notify(news:String):void
}
interface IObserver {
update(news:string):void
}
class CR7SocialMedia implements ISubject {
private observers:Observer[] = [];
subscribe(observer:Observer) {
this.observers.push(observer)
}
unsubscribe(observer:Observer) {
this.observers = this.observers.filter((element)=>{
return observer.name === element.name
})
}
notify(news:string) {
this.observers.forEach(observer => {
observer.update(news);
})
}
}
class Observer implements IObserver {
constructor(public readonly name:string) {}
private feed:string[] = [];
update(news:string) {
this.feed.push(news)
console.log(`${this.name} recieved a news`)
}
showFeed() {
console.log(this.name + ":" + this.feed)
}
}
const firstFan = new Observer("alice");
const secondFan = new Observer("bob");
const cr7 = new CR7SocialMedia();
cr7.subscribe(firstFan);
cr7.subscribe(secondFan);
cr7.notify("CR7 has sent off");
cr7.unsubscribe(secondFan);
cr7.notify("CR7 scored a goal against Inter Milan.");
firstFan.showFeed();
secondFan.showFeed()
想象一下,我们有 CR7 的社交页面,我们想关注这个页面的新闻。每个想关注这个页面的人都应该订阅他,每次有新闻的时候,我们都会调用 notify 函数,然后我们传递新闻,这个函数将新闻传递给关注者,他们的更新实现可以很容易地将这个新闻添加到他们的 feed 数组中。之后,通过调用 showFeed 函数我们可以看到这些关注者的 feed。
这就是我们应该在控制台看到的输出:
[LOG]: "alice recieved a news"
[LOG]: "bob recieved a news"
[LOG]: "bob recieved a news"
[LOG]: "alice:CR7 has sent off"
[LOG]: "bob:CR7 has sent off,CR7 scored a goal against Inter Milan."
这是对这种设计模式的一个简单实现。就我个人而言,我只有在看到一些例子时才能理解这种东西,这篇文章的目的是通过使用简单的例子和用法来解释这种设计模式。