观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象的状态发生变化时,它会通知所有观察者对象,使它们能够自动更新自己。
在观察者模式中,主题对象相当于被观察者,它维护着一个观察者列表,该列表存储着所有依赖于该主题的观察者对象。当主题对象的状态发生变化时,它会遍历观察者列表,依次调用每个观察者对象的更新方法,从而使所有的观察者对象都能够得到通知,并进行相应的处理。
观察者模式可以用于很多场景,例如:
- 网页事件绑定,如按钮点击事件、输入框输入事件等;
- MVC 架构中的视图与模型,当模型发生变化时,视图需要自动更新;
- 数据库连接池,连接池中的连接对象可以作为被观察者,当连接对象可用时,通知所有观察者。
观察者模式的优点是解耦,使得主题对象和观察者对象之间的耦合度降低,从而使得系统更加灵活。同时,观察者模式也可以实现广播通信,当一个对象发生变化时,可以将消息广播给所有观察者对象,从而避免手动通知每个观察者对象的繁琐工作。
观察者模式与发布订阅模式的区别
发布订阅模式和观察者模式是两种常见的设计模式,它们都是用于实现对象间的松耦合通信。
观察者模式中,观察者对象(也叫订阅者或监听器)直接订阅(注册)主题对象的事件,主题对象在发生变化时主动通知(调用)观察者对象的回调函数,观察者对象只需要关心主题对象的变化即可。
而发布订阅模式中,发布者对象(也叫主题对象)不直接通知订阅者对象,而是把消息交给一个调度中心(也叫消息队列或者事件通道),由调度中心统一分发消息给订阅者对象。订阅者对象需要订阅调度中心,也就是向调度中心注册自己所关心的消息类型,当调度中心有相关消息时,自动通知订阅者对象。
因此,发布订阅模式中增加了一个调度中心,用于解耦发布者和订阅者之间的关系,使得发布者和订阅者都不需要知道对方的存在。观察者模式中,主题对象和观察者对象之间是直接关联的,彼此知道对方的存在。
简单来说,观察者模式是一对一的通信,而发布订阅模式是一对多的通信。
TypeScript 实现
- 定义观察者接口(Observer):定义一个接口,该接口中包含一个更新(update)方法,用于通知观察者。
interface Observer {
update(): void;
}
- 定义主题接口(Subject):定义一个接口,该接口中包含添加观察者、删除观察者和通知观察者方法。
interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(): void;
}
- 实现具体的观察者(ConcreteObserver):实现 Observer 接口,用于接收和处理通知。
class ConcreteObserver implements Observer {
update(): void {
console.log("ConcreteObserver is notified");
}
}
- 实现具体的主题(ConcreteSubject):实现 Subject 接口,用于添加、删除和通知观察者。
class ConcreteSubject implements Subject {
private observers: Observer[] = [];
attach(observer: Observer): void {
this.observers.push(observer);
}
detach(observer: Observer): void {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notify(): void {
for (const observer of this.observers) {
observer.update();
}
}
}
- 使用观察者模式:实例化一个具体主题,然后实例化多个具体观察者,并将它们注册到主题中。当主题发生变化时,调用 notify 方法通知所有的观察者。
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();
subject.attach(observer1);
subject.attach(observer2);
subject.notify();
// Output:
// ConcreteObserver is notified
// ConcreteObserver is notified
subject.detach(observer1);
subject.notify();
// Output:
// ConcreteObserver is notified
以上是一个简单的 TypeScript 实现观察者模式的例子。需要注意的是,在实际应用中可能需要更多的代码来处理各种复杂情况,比如观察者的执行顺序等。
JavaScript 实现
// 定义观察者对象
function Observer() {
this.handlers = [];
}
Observer.prototype = {
subscribe: function(fn) {
this.handlers.push(fn);
},
unsubscribe: function(fn) {
this.handlers = this.handlers.filter(
function(item) {
if (item !== fn) {
return item;
}
}
);
},
notify: function(msg) {
this.handlers.forEach(function(fn) {
fn(msg);
});
}
}
// 定义被观察者对象
function Subject() {
this.observers = new Observer();
}
Subject.prototype = {
addListener: function(fn) {
this.observers.subscribe(fn);
},
removeListener: function(fn) {
this.observers.unsubscribe(fn);
},
notify: function(msg) {
this.observers.notify(msg);
}
}
// 创建被观察者对象
var subject = new Subject();
// 创建观察者对象
var observer1 = function(msg) {
console.log('Observer 1 received:', msg);
}
var observer2 = function(msg) {
console.log('Observer 2 received:', msg);
}
// 添加观察者
subject.addListener(observer1);
subject.addListener(observer2);
// 发送通知
subject.notify('Hello, World!');
// 移除观察者
subject.removeListener(observer2);
// 发送通知
subject.notify('Hello, World again!');
在上面的代码中,Observer 对象表示观察者,Subject 对象表示被观察者。Observer 对象有 subscribe、unsubscribe 和 notify 三个方法,分别用于订阅、取消订阅和通知观察者。Subject 对象有 addListener、removeListener 和 notify 三个方法,分别用于添加观察者、移除观察者和通知被观察者。通过这种方式,观察者可以订阅被观察者,并在其状态发生改变时得到通知。