行为型设计模式关注对象之间的通信、职责分配以及算法的封装。本章将介绍JavaScript中常用的行为型设计模式。
观察者模式定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
return () => {
this.observers = this.observers.filter(o => o !== observer);
};
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} 收到更新: ${JSON.stringify(data)}`);
}
}
const subject = new Subject();
const observer1 = new Observer('观察者1');
const observer2 = new Observer('观察者2');
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notify({ message: '新消息' });
class EventEmitter {
constructor() {
this.events = new Map();
}
on(event, listener) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event).push(listener);
return () => this.off(event, listener);
}
off(event, listener) {
if (!this.events.has(event)) return;
const listeners = this.events.get(event);
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
}
emit(event, ...args) {
if (!this.events.has(event)) return;
this.events.get(event).forEach(listener => {
listener.apply(this, args);
});
}
once(event, listener) {
const wrapper = (...args) => {
listener.apply(this, args);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
removeAllListeners(event) {
if (event) {
this.events.delete(event);
} else {
this.events.clear();
}
}
}
const emitter = new EventEmitter();
emitter.on('login', (user) => {
console.log(`用户 ${user} 登录了`);
});
emitter.once('firstVisit', () => {
console.log('首次访问');
});
emitter.emit('login', '张三');
emitter.emit('firstVisit');
emitter.emit('firstVisit');
class Store {
constructor(reducer, initialState) {
this.reducer = reducer;
this.state = initialState;
this.listeners = [];
}
getState() {
return this.state;
}
dispatch(action) {
this.state = this.reducer(this.state, action);
this.listeners.forEach(listener => listener(this.state));
return action;
}
subscribe(listener) {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
}
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'SET':
return { count: action.payload };
default:
return state;
}
}
const store = new Store(counterReducer, { count: 0 });
store.subscribe((state) => {
console.log(`计数器: ${state.count}`);
});
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'SET', payload: 100 });
发布订阅模式使用消息队列来解耦发布者和订阅者。
class PubSub {
constructor() {
this.topics = new Map();
this.subId = 0;
}
subscribe(topic, handler) {
if (!this.topics.has(topic)) {
this.topics.set(topic, new Map());
}
const id = ++this.subId;
this.topics.get(topic).set(id, handler);
return {
unsubscribe: () => {
this.topics.get(topic)?.delete(id);
}
};
}
publish(topic, data) {
const subscribers = this.topics.get(topic);
if (!subscribers) return;
subscribers.forEach(handler => handler(data));
}
publishAsync(topic, data) {
setTimeout(() => this.publish(topic, data), 0);
}
}
const pubsub = new PubSub();
const sub1 = pubsub.subscribe('news', (data) => {
console.log(`订阅者1: ${data.title}`);
});
const sub2 = pubsub.subscribe('news', (data) => {
console.log(`订阅者2: ${data.title}`);
});
pubsub.publish('news', { title: '重大新闻', content: '...' });
sub1.unsubscribe();
策略模式定义一系列算法,把它们封装起来,并使它们可以相互替换。
class PaymentStrategy {
pay(amount) {
throw new Error('必须实现 pay 方法');
}
}
class AlipayStrategy extends PaymentStrategy {
pay(amount) {
console.log(`支付宝支付 ${amount} 元`);
return { success: true, method: 'alipay' };
}
}
class WechatPayStrategy extends PaymentStrategy {
pay(amount) {
console.log(`微信支付 ${amount} 元`);
return { success: true, method: 'wechat' };
}
}
class CreditCardStrategy extends PaymentStrategy {
constructor(cardNumber) {
super();
this.cardNumber = cardNumber;
}
pay(amount) {
console.log(`信用卡 ${this.cardNumber.slice(-4)} 支付 ${amount} 元`);
return { success: true, method: 'credit_card' };
}
}
class PaymentContext {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
executePayment(amount) {
return this.strategy.pay(amount);
}
}
const payment = new PaymentContext(new AlipayStrategy());
payment.executePayment(100);
payment.setStrategy(new WechatPayStrategy());
payment.executePayment(200);
const validationStrategies = {
required: {
validate: (value) => value !== '' && value !== null && value !== undefined,
message: '此字段必填'
},
minLength: {
validate: (value, min) => value.length >= min,
message: (min) => `长度不能少于${min}个字符`
},
maxLength: {
validate: (value, max) => value.length <= max,
message: (max) => `长度不能超过${max}个字符`
},
email: {
validate: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
message: '请输入有效的邮箱地址'
},
phone: {
validate: (value) => /^1[3-9]\d{9}$/.test(value),
message: '请输入有效的手机号'
},
range: {
validate: (value, [min, max]) => value >= min && value <= max,
message: (min, max) => `值必须在${min}到${max}之间`
}
};
class Validator {
constructor() {
this.rules = [];
}
addRule(value, strategy, ...params) {
this.rules.push({ value, strategy, params });
return this;
}
validate() {
const errors = [];
for (const rule of this.rules) {
const { value, strategy, params } = rule;
const validator = validationStrategies[strategy];
if (!validator.validate(value, ...params)) {
const message = typeof validator.message === 'function'
? validator.message(...params)
: validator.message;
errors.push({ strategy, message });
}
}
return {
valid: errors.length === 0,
errors
};
}
}
const validator = new Validator();
const result = validator
.addRule('test@example.com', 'required')
.addRule('test@example.com', 'email')
.addRule('13800138000', 'phone')
.addRule('abc', 'minLength', 5)
.validate();
console.log(result);
命令模式将请求封装为对象,从而可用不同的请求对客户进行参数化。
class Command {
execute() { throw new Error('必须实现 execute'); }
undo() { throw new Error('必须实现 undo'); }
}
class Light {
on() { console.log('灯开了'); }
off() { console.log('灯关了'); }
}
class LightOnCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() { this.light.on(); }
undo() { this.light.off(); }
}
class LightOffCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() { this.light.off(); }
undo() { this.light.on(); }
}
class RemoteControl {
constructor() {
this.commands = [];
this.history = [];
}
setCommand(command) {
this.commands.push(command);
}
pressButton(index) {
const command = this.commands[index];
if (command) {
command.execute();
this.history.push(command);
}
}
undo() {
const command = this.history.pop();
if (command) {
command.undo();
}
}
}
const light = new Light();
const remote = new RemoteControl();
remote.setCommand(new LightOnCommand(light));
remote.setCommand(new LightOffCommand(light));
remote.pressButton(0);
remote.pressButton(1);
remote.undo();
class MacroCommand extends Command {
constructor(commands) {
super();
this.commands = commands;
}
execute() {
this.commands.forEach(cmd => cmd.execute());
}
undo() {
[...this.commands].reverse().forEach(cmd => cmd.undo());
}
}
class TextEditor {
constructor() {
this.content = '';
}
write(text) {
this.content += text;
console.log(`写入: "${text}",内容: "${this.content}"`);
}
delete(count) {
const deleted = this.content.slice(-count);
this.content = this.content.slice(0, -count);
console.log(`删除: "${deleted}",内容: "${this.content}"`);
return deleted;
}
}
class WriteCommand extends Command {
constructor(editor, text) {
super();
this.editor = editor;
this.text = text;
}
execute() { this.editor.write(this.text); }
undo() { this.editor.delete(this.text.length); }
}
class DeleteCommand extends Command {
constructor(editor, count) {
super();
this.editor = editor;
this.count = count;
this.deleted = '';
}
execute() { this.deleted = this.editor.delete(this.count); }
undo() { this.editor.write(this.deleted); }
}
迭代器模式提供一种方法顺序访问聚合对象中的元素,而不暴露其内部表示。
class Iterator {
constructor(collection) {
this.collection = collection;
this.index = 0;
}
next() {
if (this.hasNext()) {
return {
value: this.collection[this.index++],
done: false
};
}
return { value: undefined, done: true };
}
hasNext() {
return this.index < this.collection.length;
}
reset() {
this.index = 0;
}
}
class Aggregate {
constructor(items) {
this.items = items;
}
createIterator() {
return new Iterator(this.items);
}
}
const aggregate = new Aggregate([1, 2, 3, 4, 5]);
const iterator = aggregate.createIterator();
while (iterator.hasNext()) {
console.log(iterator.next().value);
}
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
const step = this.step;
return {
next() {
if (current < end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const range = new Range(0, 10, 2);
for (const num of range) {
console.log(num);
}
const rangeArray = [...range];
class Tree {
constructor(value, left = null, right = null) {
this.value = value;
this.left = left;
this.right = right;
}
*[Symbol.iterator]() {
yield this.value;
if (this.left) yield* this.left;
if (this.right) yield* this.right;
}
*inOrder() {
if (this.left) yield* this.left.inOrder();
yield this.value;
if (this.right) yield* this.right.inOrder();
}
}
const tree = new Tree(4,
new Tree(2, new Tree(1), new Tree(3)),
new Tree(6, new Tree(5), new Tree(7))
);
console.log('前序:', [...tree]);
console.log('中序:', [...tree.inOrder()]);
状态模式允许对象在其内部状态改变时改变其行为。
class State {
constructor(name) {
this.name = name;
}
handle(context) {
throw new Error('必须实现 handle 方法');
}
}
class OrderState extends State {
static PENDING = new OrderState('待支付');
static PAID = new OrderState('已支付');
static SHIPPED = new OrderState('已发货');
static DELIVERED = new OrderState('已送达');
static CANCELLED = new OrderState('已取消');
}
class PendingState extends State {
constructor() { super('待支付'); }
pay(context) {
console.log('支付成功');
context.setState(new PaidState());
}
cancel(context) {
console.log('订单已取消');
context.setState(new CancelledState());
}
}
class PaidState extends State {
constructor() { super('已支付'); }
ship(context) {
console.log('商品已发货');
context.setState(new ShippedState());
}
cancel(context) {
console.log('申请退款中...');
context.setState(new CancelledState());
}
}
class ShippedState extends State {
constructor() { super('已发货'); }
deliver(context) {
console.log('商品已送达');
context.setState(new DeliveredState());
}
}
class DeliveredState extends State {
constructor() { super('已送达'); }
confirm(context) {
console.log('订单已完成');
}
}
class CancelledState extends State {
constructor() { super('已取消'); }
}
class Order {
constructor() {
this.state = new PendingState();
}
setState(state) {
this.state = state;
}
pay() {
if (this.state.pay) {
this.state.pay(this);
} else {
console.log('当前状态不支持支付操作');
}
}
ship() {
if (this.state.ship) {
this.state.ship(this);
} else {
console.log('当前状态不支持发货操作');
}
}
deliver() {
if (this.state.deliver) {
this.state.deliver(this);
} else {
console.log('当前状态不支持送达操作');
}
}
cancel() {
if (this.state.cancel) {
this.state.cancel(this);
} else {
console.log('当前状态不支持取消操作');
}
}
getStatus() {
return this.state.name;
}
}
const order = new Order();
console.log(order.getStatus());
order.pay();
console.log(order.getStatus());
order.ship();
console.log(order.getStatus());
责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
class Handler {
constructor() {
this.next = null;
}
setNext(handler) {
this.next = handler;
return handler;
}
handle(request) {
if (this.next) {
return this.next.handle(request);
}
return null;
}
}
class AuthHandler extends Handler {
handle(request) {
if (!request.user) {
return { success: false, message: '未登录' };
}
console.log('认证通过');
return super.handle(request);
}
}
class PermissionHandler extends Handler {
handle(request) {
if (!request.user.permissions.includes('write')) {
return { success: false, message: '无权限' };
}
console.log('权限验证通过');
return super.handle(request);
}
}
class ValidationHandler extends Handler {
handle(request) {
if (!request.data || !request.data.title) {
return { success: false, message: '数据验证失败' };
}
console.log('数据验证通过');
return super.handle(request);
}
}
class SaveHandler extends Handler {
handle(request) {
console.log('保存数据');
return { success: true, message: '保存成功' };
}
}
const auth = new AuthHandler();
const permission = new PermissionHandler();
const validation = new ValidationHandler();
const save = new SaveHandler();
auth.setNext(permission).setNext(validation).setNext(save);
const result = auth.handle({
user: { id: 1, permissions: ['write'] },
data: { title: '测试文章' }
});
console.log(result);
class Middleware {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
return this;
}
async execute(context) {
let index = 0;
const next = async () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
await middleware(context, next);
}
};
await next();
return context;
}
}
const app = new Middleware();
app.use(async (ctx, next) => {
console.log('中间件1 - 开始');
ctx.startTime = Date.now();
await next();
console.log('中间件1 - 结束');
});
app.use(async (ctx, next) => {
console.log('中间件2 - 处理');
ctx.data = '处理后的数据';
await next();
});
app.use(async (ctx, next) => {
console.log('中间件3 - 响应');
ctx.response = { success: true, data: ctx.data };
await next();
});
app.execute({}).then(ctx => {
console.log('最终结果:', ctx.response);
});
模板方法模式定义一个操作中的算法骨架,将一些步骤延迟到子类中。
class DataParser {
parse(data) {
const rawData = this.read(data);
const parsedData = this.process(rawData);
const result = this.format(parsedData);
this.validate(result);
return result;
}
read(data) {
throw new Error('子类必须实现 read 方法');
}
process(data) {
return data;
}
format(data) {
throw new Error('子类必须实现 format 方法');
}
validate(result) {
console.log('验证结果...');
}
}
class JSONParser extends DataParser {
read(data) {
console.log('读取JSON数据');
return JSON.parse(data);
}
format(data) {
console.log('格式化JSON');
return { type: 'json', data };
}
}
class XMLParser extends DataParser {
read(data) {
console.log('读取XML数据');
return this.parseXML(data);
}
parseXML(xmlString) {
return { parsed: xmlString };
}
format(data) {
console.log('格式化XML');
return { type: 'xml', data };
}
}
class CSVParser extends DataParser {
read(data) {
console.log('读取CSV数据');
return data.split('\n').map(row => row.split(','));
}
format(data) {
console.log('格式化CSV');
const headers = data[0];
const rows = data.slice(1);
return rows.map(row => {
const obj = {};
headers.forEach((h, i) => obj[h] = row[i]);
return obj;
});
}
}
🔄 观察者 vs 发布订阅
- 观察者模式:Subject直接通知Observer,双方有直接联系
- 发布订阅模式:通过消息队列解耦,发布者和订阅者不知道对方存在
发布订阅模式更适合分布式系统和微服务架构
⛓️ 责任链模式应用场景
- 事件冒泡和捕获
- 中间件系统(Express、Koa)
- 审批流程
- 异常处理
- 日志处理
下一章将探讨 [Web Workers](file:///e:/db-w.cn/md_data/javascript/70_Web Workers.md),学习如何在浏览器中实现多线程编程。