行为型模式

行为型设计模式关注对象之间的通信、职责分配以及算法的封装。本章将介绍JavaScript中常用的行为型设计模式。

观察者模式(Observer)

观察者模式定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。

基本实现

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: '新消息' });

EventEmitter实现

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 });

发布订阅模式(Pub/Sub)

发布订阅模式使用消息队列来解耦发布者和订阅者。

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();

策略模式(Strategy)

策略模式定义一系列算法,把它们封装起来,并使它们可以相互替换。

基本实现

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);

命令模式(Command)

命令模式将请求封装为对象,从而可用不同的请求对客户进行参数化。

基本实现

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); }
}

迭代器模式(Iterator)

迭代器模式提供一种方法顺序访问聚合对象中的元素,而不暴露其内部表示。

基本实现

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()]);

状态模式(State)

状态模式允许对象在其内部状态改变时改变其行为。

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());

责任链模式(Chain of Responsibility)

责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。

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);
});

模板方法模式(Template Method)

模板方法模式定义一个操作中的算法骨架,将一些步骤延迟到子类中。

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),学习如何在浏览器中实现多线程编程。