设计模式学习之-观察者模式

134 阅读4分钟

前言:

观察者模式(Observer Pattern)是一种非常常见的设计模式,广泛应用于前端开发中,尤其是在响应式系统事件驱动架构中。它的主要思想是,当一个对象的状态发生变化时,所有依赖于该对象的观察者都会接收到通知并执行一系列的操作。本文将详细介绍观察者模式的概念、应用场景,以及如何在 JavaScript 中实现这一模式。

1. 观察者模式简介

观察者模式涉及两个主要角色:

  • 观察者(Observer):依赖于某个对象状态的对象。当目标对象状态发生变化时,观察者会收到通知并执行相应的更新操作。
  • 目标(Subject):持有状态管理观察者的对象。当其状态发生变化时,通知多有观察者。

工作原理

  1. 目标对象 提供一个注册观察者的方法,允许观察者订阅它的状态变化。
  2. 目标对象的状态 发生变化时,它会通知所有注册的 观察者
  3. 观察者 在接收到通知后,执行特定的逻辑以更新其状态。

这种模式可以帮助解耦观察者与目标,使得当某个对象的状态发生变化时,不需要修改其他依赖于它的对象。这样就降低了系统的耦合性,增强了代码的可维护性和可扩展性。

2. 观察者模式的应用场景

在前端开发中,观察者模式应用十分广泛,以下是一些常见的应用场景:

  • 事件系统:Javascript的DOM事件监听机制就是一种观察者模式的实现,例如 addEventListener
  • 数据绑定:Vue和React等前端框架中,组件的数据更新和视图的重新渲染是基于观察者模式的。
  • 发布-订阅模式:观察者模式是发布-订阅模式的基础,例如Node.js中的EventEmitter
  • 实时更新:例如即时通讯(IM)聊天室应用,当有新消息时,所有参与者都会实时收到更新内容。
  • MVVM模式中的双向绑定:在MVVM(Model-View-ViewModel)模式中,视图(View)和视图模型(ViewModel)之间通常通过双向数据绑定来进行通信。当模型中的数据发生变化时,视图会自动更新,反之亦然。Vue和Angular等前端框架中的数据绑定机制就是基于观察者模式实现的。

3. 在JavaScript中的实现观察者模式

接下来我们将通过一个简单的例子来实现观察者模式,展示如何在JavaScript中构建目标和观察者。

目标类(Sublect)

首先,我们创建一个 Subject 类,用来储存状态并允许观察者订阅、取消订阅以及通知等操作。

class Subject {
  constructor() {
    this.observers = [];  // 存储观察者
  }

  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }

  // 移除观察者
  removeObserver(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  // 通知所有观察者
  notifyObservers(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

观察者类(Observer)

观察者类需要有一个 update方法,当它被通知时,会执行相应的逻辑。

class Observer {
  constructor(name) {
    this.name = name;
  }

  // 当通知到来时执行更新
  update(data) {
    console.log(`${this.name} 收到了通知:`, data);
  }
}

使用示例

接下来,我们可以创造一个Subject实例和多个观察者,并模拟状态变化时,通知更新的整个过程。

const subject = new Subject();

// 创建观察者
const observer1 = new Observer('观察者 1');
const observer2 = new Observer('观察者 2');

// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);

// 模拟状态更新并通知所有观察者
subject.notifyObservers('状态发生变化');

// 移除一个观察者
subject.removeObserver(observer1);

// 再次通知所有观察者
subject.notifyObservers('状态再次变化');

控制台输出内容:

观察者 1 收到了通知: 状态发生变化
观察者 2 收到了通知: 状态发生变化
观察者 2 收到了通知: 状态再次变化

从这个例子中可以看出,当Subject的状态发生变化时,所有注册的观察者都会接收到通知并执行他们的update方法。

4. 优点和缺点

优点

  • 解耦:观察者模式将目标与观察者解耦,使得他们可以独立发展,增加了代码的可维护性和灵活性。
  • 动态订阅:观察者可以在运行时动态添加或者移除,系统具有很好的扩展性。
  • 推送更新:目标对象可以主动通知观察者,减少了观察者定期检查目标状态的开销。

缺点

  • 过度通知:如果观察者过多,目标对象的状态变化频繁,可能导致过度通知,影响性能。
  • 复杂性增加:对于复杂的系统,维护观察者和目标之间的关系可能会增加额外的复杂性。

5. 实际应用中的扩展

在实际开发中,观察者模式通常会被扩展为 发布-订阅模式 (Publish-Subscribe),这种模式通过引入一个消息通道进一步解耦发布者和订阅者,使它们不直接依赖于彼此。在Vue总,这种发布订阅模式可以通过Event Bus来实现,常用于不同组件之间的通信。

Vue中的发布-订阅模式

示例代码
1. 创建Event Bus

首先,我们可以创建一个单独的文件作为事件总线:

// event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
2. 发布事件

在需要发布事件的组件中,调用EventBus.$emit()来触发事件:

// 发布事件的组件 (ComA.vue)
<template>
  <button @click="sendMessage">发送消息</button>
</template>

<script>
import { EventBus } from './event-bus';

export default {
  methods: {
    sendMessage() {
      EventBus.$emit('message-sent', 'Hello from ComponentA');
    }
  }
}
</script>
3. 订阅事件

在需要监听事件的组件中,使用EventBus.$on()来监听并处理相关事件:

// 订阅事件的组件 (ComB.vue)
<template>
  <div>收到的消息:{{ message }}</div>
</template>

<script>
import { EventBus } from './event-bus';

export default {
  data() {
    return {
      message: ''
    };
  },
  mounted() {
    EventBus.$on('message-sent', (msg) => {
      this.message = msg;
    });
  }
}
</script>
4. 小结

通过Event Bus,Vue实现了一个简单的发布-订阅模式。组件可以通过它发布消息,其他组件可以订阅这些消息并作出相应的反应。这种模式很好地解耦了组件之间的依赖,特别是在大型应用中,能够有效地管理组件通信。

6. 总结

观察者模式作为一种行为型设计模式,广泛应用于JavaScript开发中,尤其是事件处理、数据绑定和发布-订阅模式中。通过它,开发者能够轻松管理对象之间的依赖关系,使代码更加灵活、可扩展。在日常开发中,理解和灵活运用观察者模式,可以极大提升系统的可维护性和响应性。