观察者模式,是JavaScript设计模式之一。当然也不仅仅限于JavaScript这门语言,网上对该模式的介绍已是多如牛毛,而且讲得各有特色各有心得。即便如此,笔者仍精心准备了这篇博客,期望用最简单的方式来介绍下该模式。
首先来看下维基百科对 观察者模式 的解释:
观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
换个角度讲:
观察者模式是一种行为型设计模式,它建立了一个一对多的依赖关系。在这种模式中,一个对象(称为主题或被观察者)维护了一组依赖于它的对象(称为观察者),并在自身状态发生变化时主动通知观察者。这种通知通常通过调用观察者的特定方法来实现。
其实笔者更倾向于它的另一个名字发布/订阅模式(Publish/Subscribe) ,因为更能表达出该模式的核心思路,那就是:发布 和订阅两个过程。
讲到这可能会给大家一种讲了但是又没讲的感觉,为了更好地理解观察者模式,让我们用一个实际场景来说明它。假设我们要开发一个天气发布网站,而且支持用户订阅天气信息。那么这个场景下,观察者模式可以派上用场。
首先我们先要根据实际场景提炼出两个核心要素:
对象:在这个场景中,我们有两种对象,一是用户(观察者),二是天气数据提供者(主题)。
行为:用户可以订阅城市的天气更新,取消订阅,而天气数据提供者可以发布新的天气信息。
接下来我们就开始用代码一步一步来实现这个需求:
首先我们定义一个天气数据提供者发布者
js
class WeatherCenter {
constructor() {
this.observers = []; // 观察者列表
this.weather = null; // 当前天气信息
}
}
那么一起来按照订阅的过程想象下,发布者具有哪些属性或者方法?
- 首先,我们订阅网站的天气信息,是不是需要给用户提供订阅入口?那么
WeatherCenter
中必定会有一个方法提供给我们实现订阅
。 - 其次,如果网站要向订阅者们发送天气信息,是不是需要一个方法去做?那么
WeatherCenter
中必定会有一个方法实现发布
。 - 再然后,如例子所说如果我突然不想订阅了,那么
WeatherCenter
中必定会有一个方法提供给我们实现取消订阅
。
说到这里一切都了然了,下面还是讲想象到的东西用代码表达出来吧。
js
class WeatherCenter {
constructor() {
this.observers = []; // 观察者列表
this.weather = null; // 当前天气信息
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
setWeather(weather) {
this.weather = weather;
this.notifyObservers();
}
notifyObservers() {
this.observers.forEach(observer => {
observer.update(this.weather);
});
}
}
在上面的代码中,WeatherCenter
类代表了天气数据提供者,它维护了一个观察者列表和当前的天气信息。它提供了 addObserver
、removeObserver
、setWeather
和 notifyObservers
方法,用于管理观察者和通知它们。
接下来,我们创建一个观察者对象,以便订阅天气信息。其实这个对象逻辑很简单,就是提供一个方法供WeatherCenter
对象来调用并传入一些信息。
js
class WeatherObserver {
constructor(name) {
this.name = name;
}
update(weather) {
console.log(`${this.name}收到天气更新:${weather}`);
}
}
到这里,笔者所要表达的内容已全部实现。接下来我们就看下最终的效果吧,全部代码如下:
js
class WeatherCenter {
constructor() {
this.observers = []; // 观察者列表
this.weather = null; // 当前天气信息
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
setWeather(weather) {
this.weather = weather;
this.notifyObservers();
}
notifyObservers() {
this.observers.forEach(observer => {
observer.update(this.weather);
});
}
}
class WeatherObserver {
constructor(name) {
this.name = name;
}
update(weather) {
console.log(`${this.name}收到天气更新:${weather}`);
}
}
const weatherCenter = new WeatherCenter();
const user1 = new WeatherObserver("用户1");
const user2 = new WeatherObserver("用户2");
weatherCenter.addObserver(user1);
weatherCenter.addObserver(user2);
weatherCenter.setWeather("晴天");
weatherCenter.setWeather("多云");
运行效果如我们所预料的那样:

在这个示例中,我们创建了一个天气数据提供者 WeatherCenter
和两个观察者 user1
和 user2
。然后,我们将观察者添加到主题中,并通过调用 setWeather
方法来更新天气信息。
当 setWeather
方法被调用时,它会通知所有的观察者,并触发它们的 update
方法,从而使观察者知道天气信息的变化。
通过这个简单的示例,您可以看到观察者模式如何在JavaScript中实现,以及如何帮助我们构建松耦合的应用程序组件。观察者模式使得主题和观察者之间的关系更加灵活,有助于构建可扩展的应用程序。
最后我们总结一下观察者模式的特点和使用注意事项:
优点
松耦合: 观察者模式可以帮助实现松耦合的对象之间的通信。主题对象和观察者对象之间的关系是一对多的,主题不需要知道观察者的具体细节,只需知道它们实现了特定的接口或方法
可维护性和可扩展性: 由于对象之间的松耦合性,观察者模式使得系统更容易维护和扩展。您可以轻松地添加新的观察者或修改现有的观察者,而不需要修改主题
通知机制: 观察者模式提供了一种通知机制,当主题的状态发生变化时,所有观察者都会得到通知并及时更新。这在实时性要求较高的应用程序中非常有用。
缺点过多的通知: 当主题频繁地发生变化时,会导致观察者接收大量的通知,这可能会引起性能问题。因此,在某些情况下,需要谨慎使用观察者模式。
可能引发循环依赖: 如果观察者之间相互依赖,可能导致循环依赖的问题,这会增加代码的复杂性并可能引发不稳定的行为。
使用注意事项选择合适的场景: 观察者模式适用于当一个对象的状态变化需要通知其他多个对象,并且这些对象的具体数量和类型可能会在运行时变化时。因此,在使用观察者模式之前,需要仔细考虑是否有这样的需求。
避免过多通知: 确保只有重要的状态变化才会通知观察者,以避免性能问题。可以使用延迟通知或者批量通知来优化。
处理异常: 在观察者模式中,观察者可能会抛出异常,因此需要适当地处理异常,以确保不会影响其他观察者和主题。
避免循环依赖: 在设计观察者模式时,要确保避免观察者之间的循环依赖,这可能会导致不可预测的问题。
总之,观察者模式是一种强大的设计模式,它在许多情况下都非常有用。然而,在使用时需要权衡其优点和缺点,并根据具体的应用场景来决定是否使用以及如何使用。正确使用观察者模式可以帮助构建松耦合、可维护和可扩展的软件系统