概述
观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
问题
假如你有两种类型的对象: 顾客和 商店 。 顾客对某个特定品牌的产品非常感兴趣 (例如最新型号的 iPhone 手机), 而该产品很快将会在商店里出售。
顾客可以每天来商店看看产品是否到货。 但如果商品尚未到货时, 绝大多数来到商店的顾客都会空手而归。
解决方案
拥有一些值得关注的状态的对象通常被称为目标, 由于它要将自身的状态改变通知给其他对象, 我们也将其称为发布者 (publisher)。 所有希望关注发布者状态变化的其他对象被称为订阅者 (subscribers)。
观察者模式建议你为发布者类添加订阅机制, 让每个对象都能订阅或取消订阅发布者事件流。 不要害怕! 这并不像听上去那么复杂。 实际上, 该机制包括 1) 一个用于存储订阅者对象引用的列表成员变量; 2) 几个用于添加或删除该列表中订阅者的公有方法。
TS代码实现
/* 观察者模式 */
/**
* 发布者
*/
interface Subject {
// 订阅
attach(observer: Observer): void;
// 取消订阅
detach(observer: Observer): void;
// 通知
notify(): void;
}
/**
* 发布者拥有一些重要的状态,并在状态发生变化时通知观察者。
*/
class ConcreteSubject implements Subject {
/**
* @type {number} 状态 数据
*/
public state: number;
/**
* @type {Observer[]} 观察者们(订阅者们)
*/
private observers: Observer[] = [];
/**
* 订阅方法
*/
public attach(observer: Observer): void {
// 是否存在
const isExist = this.observers.includes(observer);
if (isExist) {
return console.log('发布者:已经订阅啦');
}
console.log('发布者:订阅成功');
this.observers.push(observer);
}
public detach(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
if (observerIndex === -1) {
return console.log('发布者:没找到该订阅者');
}
this.observers.splice(observerIndex, 1);
console.log('发布者:取消订阅');
}
/**
* 通知
*/
public notify(): void {
console.log('发布者:通知观察者们更新');
for (const observer of this.observers) {
observer.update(this);
}
}
/**
* 特定业务逻辑触发通知方法
*/
public someBusinessLogic(): void {
console.log('\nSubject: I'm doing something important.');
this.state = Math.floor(Math.random() * (10 + 1));
console.log(`Subject: My state has just changed to: ${this.state}`);
this.notify();
}
}
/**
* 观察者
*/
interface Observer {
// Receive update from subject. 更新
update(subject: Subject): void;
}
/**
* 观察者
*/
class ConcreteObserverA implements Observer {
public update(subject: Subject): void {
if (subject instanceof ConcreteSubject && subject.state < 3) {
console.log('状态小于三');
}
}
}
class ConcreteObserverB implements Observer {
public update(subject: Subject): void {
if (subject instanceof ConcreteSubject && (subject.state === 0 || subject.state >= 2)) {
console.log('等于0 或大于等于2');
}
}
}
/**
* The client code.
*/
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserverA();
subject.attach(observer1);
const observer2 = new ConcreteObserverB();
subject.attach(observer2);
subject.someBusinessLogic();
subject.someBusinessLogic();
subject.detach(observer2);
subject.someBusinessLogic();
参考文件
refactoringguru.cn/design-patt…
现实生活中的例子
粉丝关注主播,主播开播后通知粉丝开播啦
/* 观察者模式 */
/**
* 发布者
*/
interface Subject {
// 订阅
attach(observer: Observer): void;
// 取消订阅
detach(observer: Observer): void;
// 通知
notify(): void;
}
/**
* 主播
*/
class AnchorSubject implements Subject {
constructor(isOnLine, name) {
this.isOnLine = isOnLine
this.name = name
}
public isOnLine: boolean; // 主播是否在线
public name: string; // 主播姓名
private observers: Observer[] = []; // 粉丝们
// 关注
public attach(observer: Observer): void {
// 是否已经关注
const isExist = this.observers.includes(observer);
if (isExist) {
return console.log(`${this.name}:你已经关注我啦,你双击关注是想取关吗!`);
}
console.log(`${this.name}:谢谢关注`);
this.observers.push(observer);
}
// 取关
public detach(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
if (observerIndex === -1) {
return console.log(`${this.name}:先关注我才能取关哦`);
}
this.observers.splice(observerIndex, 1);
console.log(`${this.name}:取关成功,等你回来`);
}
/**
* 通知
*/
public notify(): void {
if (this.isOnLine) {
console.log(`${this.name}:粉丝们我开播了`);
for (const observer of this.observers) {
observer.update(this);
}
} else {
console.log(`${this.name}:*** 耻辱下播`);
for (const observer of this.observers) {
observer.update(this);
}
}
}
/**
* 开播 上线
*/
public goOnline(): void {
this.isOnLine = true;
this.notify();
}
/**
* 耻辱下播 下线
*/
public offline(): void {
this.isOnLine = false;
this.notify();
}
}
/**
* 观察者
*/
interface Observer {
update(subject: Subject): void;
}
/**
* 粉丝
*/
class FansObserverA implements Observer {
public update(subject: Subject): void {
if (subject instanceof AnchorSubject && subject.isOnLine) {
console.log(`${subject.name}:开播啦`);
} else {
console.log(`彩笔耻辱下播了`);
}
}
}
/**
* The client code.
*/
// 创建一个主播
const subject = new AnchorSubject(false, 'YYF');
// 创建一个粉丝
const fansA = new FansObserverA();
// 关注
subject.attach(fansA);
// 主播上线
subject.goOnline();
// 取关
subject.detach(fansA);
// 关注
subject.attach(fansA);
// 关注
subject.attach(fansA);
// 主播下线
subject.offline();
运行结果
yyfyyds