logo
火山博客
导航

十大经典设计模式及其TypeScript实现

2025-01-24
14阅读时间25分钟

前言

设计模式是软件开发中解决常见问题的最佳实践方案。它们不仅能帮助我们写出更优雅、可维护的代码,还能提高代码的重用性和可扩展性。本文将介绍十个最常用的设计模式,并使用 TypeScript 来实现它们。

为什么要学习设计模式?

  1. 提高代码质量:设计模式提供了经过验证的解决方案,能帮助我们写出更好的代码
  2. 提升开发效率:熟悉设计模式后,可以更快地解决类似的问题
  3. 便于团队协作:设计模式提供了通用的解决方案词汇,有助于团队成员之间的交流
  4. 增强代码的可维护性:设计模式使代码结构更清晰,更容易维护和扩展

为什么选择TypeScript?

  1. 类型安全:TypeScript的静态类型系统可以在编译时捕获潜在错误
  2. 面向对象特性:TypeScript提供了完整的面向对象编程支持,更适合实现设计模式
  3. 更好的工具支持:TypeScript的类型系统提供了更好的IDE支持和代码提示
  4. 前后端通用:TypeScript可以在前端和Node.js环境中使用,应用范围广

如何使用本文?

本文介绍的每个设计模式都包含以下内容:

  • 模式的定义和核心思想
  • 主要特点和适用场景
  • 实现时需要注意的事项
  • 基于实际场景的代码示例

建议读者:

  1. 先理解每个模式要解决的问题
  2. 仔细阅读代码示例,理解实现原理
  3. 尝试在自己的项目中实践
  4. 注意模式之间的关系和区别

1. 单例模式 (Singleton Pattern)

单例模式确保一个类只有一个实例,并提供一个全局访问点。

主要特点

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点
  • 懒加载,第一次使用时才会实例化
  • 线程安全考虑,在多线程环境下需要特别注意实例化过程

适用场景

  • 管理共享资源(如数据库连接、线程池)
  • 全局状态管理(如配置管理器、日志记录器)
  • 控制资源的并发访问
  • 全局缓存或存储机制

注意事项

  • 注意多线程环境下的同步问题
  • 避免单例的滥用,过度使用会导致代码耦合
  • 注意单例的销毁时机,防止内存泄漏
  • 考虑单例对象的序列化问题

代码实现

Typescript
// 数据库连接类
class DatabaseConnection {
    private static instance: DatabaseConnection | null = null;
    private connectionString: string;
    private isConnected: boolean = false;

    private constructor(connectionString: string) {
        this.connectionString = connectionString;
    }

    public static getInstance(connectionString: string): DatabaseConnection {
        if (!DatabaseConnection.instance) {
            DatabaseConnection.instance = new DatabaseConnection(connectionString);
        }
        return DatabaseConnection.instance;
    }

    public connect(): void {
        if (!this.isConnected) {
            console.log(`连接到数据库: ${this.connectionString}`);
            this.isConnected = true;
        } else {
            console.log('已经连接到数据库');
        }
    }

    public disconnect(): void {
        if (this.isConnected) {
            console.log('断开数据库连接');
            this.isConnected = false;
        }
    }

    public query(sql: string): void {
        if (this.isConnected) {
            console.log(`执行查询: ${sql}`);
        } else {
            throw new Error('未连接到数据库');
        }
    }
}

// 使用示例
const db1 = DatabaseConnection.getInstance("mysql://localhost:3306/db1");
const db2 = DatabaseConnection.getInstance("mysql://localhost:3306/db1");

console.log('db1 === db2:', db1 === db2); // true

db1.connect();
// 输出: 连接到数据库: mysql://localhost:3306/db1

db2.connect();
// 输出: 已经连接到数据库

db1.query("SELECT * FROM users");
// 输出: 执行查询: SELECT * FROM users

db1.disconnect();
// 输出: 断开数据库连接

2. 工厂模式 (Factory Pattern)

工厂模式提供了创建对象的接口,让子类决定实例化哪个类。

主要特点

  • 封装对象的创建过程
  • 解耦对象的创建和使用
  • 支持扩展新的产品类型
  • 实现了创建和使用的分离

适用场景

  • 需要根据条件动态创建不同但相关的对象
  • 需要统一管理对象的创建过程
  • 对象的创建过程比较复杂
  • 需要隐藏对象的具体实现类

注意事项

  • 工厂类的设计要足够灵活以支持扩展
  • 避免创建过多的工厂类
  • 注意工厂方法的命名要清晰明确
  • 考虑是否需要抽象工厂

代码实现

Typescript
// 定义产品接口
interface Vehicle {
    start(): void;
    stop(): void;
    getInfo(): string;
}

// 具体产品:汽车
class Car implements Vehicle {
    private model: string;
    private year: number;

    constructor(model: string, year: number) {
        this.model = model;
        this.year = year;
    }

    start(): void {
        console.log(`${this.model} 汽车启动`);
    }

    stop(): void {
        console.log(`${this.model} 汽车停止`);
    }

    getInfo(): string {
        return `${this.year}${this.model} 汽车`;
    }
}

// 具体产品:摩托车
class Motorcycle implements Vehicle {
    private brand: string;
    private type: string;

    constructor(brand: string, type: string) {
        this.brand = brand;
        this.type = type;
    }

    start(): void {
        console.log(`${this.brand} 摩托车启动`);
    }

    stop(): void {
        console.log(`${this.brand} 摩托车停止`);
    }

    getInfo(): string {
        return `${this.brand} ${this.type} 摩托车`;
    }
}

// 工厂类
class VehicleFactory {
    createCar(model: string, year: number): Vehicle {
        return new Car(model, year);
    }

    createMotorcycle(brand: string, type: string): Vehicle {
        return new Motorcycle(brand, type);
    }
}

// 使用示例
const factory = new VehicleFactory();

// 创建汽车
const car = factory.createCar("Tesla Model 3", 2023);
console.log(car.getInfo()); // 输出: 2023年 Tesla Model 3 汽车
car.start(); // 输出: Tesla Model 3 汽车启动

// 创建摩托车
const motorcycle = factory.createMotorcycle("Harley-Davidson", "Street");
console.log(motorcycle.getInfo()); // 输出: Harley-Davidson Street 摩托车
motorcycle.start(); // 输出: Harley-Davidson 摩托车启动

3. 策略模式 (Strategy Pattern)

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换。

主要特点

  • 算法可以自由切换
  • 避免使用多重条件判断
  • 扩展性良好
  • 策略算法可以重用

适用场景

  • 需要在运行时切换算法的场景
  • 有多个类只区别在它们的行为时
  • 需要避免使用多重条件语句时
  • 算法需要自由切换时

注意事项

  • 策略类的数量可能会增多
  • 所有策略类都需要对外暴露
  • 注意策略切换的性能消耗
  • 确保客户端了解所有策略的区别

代码实现

Typescript
// 定义策略接口
interface PaymentStrategy {
    pay(amount: number): void;
    validate(): boolean;
}

// 具体策略:信用卡支付
class CreditCardPayment implements PaymentStrategy {
    private cardNumber: string;
    private expiryDate: string;
    private cvv: string;

    constructor(cardNumber: string, expiryDate: string, cvv: string) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
        this.cvv = cvv;
    }

    validate(): boolean {
        // 简化的验证逻辑
        return this.cardNumber.length === 16 && 
               this.cvv.length === 3;
    }

    pay(amount: number): void {
        if (this.validate()) {
            console.log(`使用信用卡支付 ${amount} 元`);
            console.log(`卡号: ${this.maskCardNumber()}`);
        } else {
            throw new Error('信用卡信息无效');
        }
    }

    private maskCardNumber(): string {
        return '****' + this.cardNumber.slice(-4);
    }
}

// 具体策略:支付宝支付
class AlipayPayment implements PaymentStrategy {
    private email: string;
    private password: string;

    constructor(email: string, password: string) {
        this.email = email;
        this.password = password;
    }

    validate(): boolean {
        // 简化的验证逻辑
        return this.email.includes('@') && 
               this.password.length >= 6;
    }

    pay(amount: number): void {
        if (this.validate()) {
            console.log(`使用支付宝支付 ${amount} 元`);
            console.log(`账号: ${this.email}`);
        } else {
            throw new Error('支付宝账号信息无效');
        }
    }
}

// 支付处理器
class PaymentProcessor {
    private strategy: PaymentStrategy;

    constructor(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy: PaymentStrategy): void {
        this.strategy = strategy;
    }

    processPayment(amount: number): void {
        this.strategy.pay(amount);
    }
}

// 使用示例
// 创建支付策略
const creditCardPayment = new CreditCardPayment("1234567890123456", "12/24", "123");
const alipayPayment = new AlipayPayment("user@example.com", "password123");

// 创建支付处理器
const paymentProcessor = new PaymentProcessor(creditCardPayment);

// 使用信用卡支付
try {
    paymentProcessor.processPayment(100);
    // 输出:
    // 使用信用卡支付 100 元
    // 卡号: ****3456
} catch (error) {
    console.error(error.message);
}

// 切换到支付宝支付
paymentProcessor.setStrategy(alipayPayment);
try {
    paymentProcessor.processPayment(200);
    // 输出:
    // 使用支付宝支付 200 元
    // 账号: user@example.com
} catch (error) {
    console.error(error.message);
}

4. 观察者模式 (Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

主要特点

  • 支持松耦合设计,观察者和被观察者之间的关系是动态的
  • 符合开闭原则,添加新的观察者不需要修改原有代码
  • 支持广播通信,一个状态改变可以通知多个观察者
  • 可以建立一对多的联动关系

适用场景

  • 实现事件处理系统(如DOM事件监听)
  • 实现状态监听和通知机制(如Vue的响应式系统)
  • 实现消息推送和订阅系统
  • 实现UI组件间的数据同步

注意事项

  • 避免循环引用,防止互相观察导致的死循环
  • 注意内存泄漏,及时取消不需要的观察者
  • 当观察者较多时,通知的性能问题需要关注
  • 状态变化通知的顺序不应该影响程序的正确性

代码实现

Typescript
// 定义观察者接口
interface Observer<T> {
    update(data: T): void;
}

// 定义主题接口
interface Subject<T> {
    attach(observer: Observer<T>): void;
    detach(observer: Observer<T>): void;
    notify(): void;
}

// 具体主题实现
class NewsAgency implements Subject<string> {
    private observers: Set<Observer<string>> = new Set();
    private news: string = '';

    attach(observer: Observer<string>): void {
        this.observers.add(observer);
    }

    detach(observer: Observer<string>): void {
        this.observers.delete(observer);
    }

    notify(): void {
        this.observers.forEach(observer => observer.update(this.news));
    }

    // 发布新闻
    publishNews(news: string): void {
        this.news = news;
        this.notify();
    }
}

// 具体观察者实现
class NewsSubscriber implements Observer<string> {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    update(news: string): void {
        console.log(`${this.name} 收到新闻: ${news}`);
    }
}

// 使用示例
const newsAgency = new NewsAgency();
const subscriber1 = new NewsSubscriber("订阅者1");
const subscriber2 = new NewsSubscriber("订阅者2");

newsAgency.attach(subscriber1);
newsAgency.attach(subscriber2);

newsAgency.publishNews("重大新闻:TypeScript 5.0 发布!");
// 输出:
// 订阅者1 收到新闻: 重大新闻:TypeScript 5.0 发布!
// 订阅者2 收到新闻: 重大新闻:TypeScript 5.0 发布!

newsAgency.detach(subscriber1);
newsAgency.publishNews("TypeScript 5.1 即将发布");
// 输出:
// 订阅者2 收到新闻: TypeScript 5.1 即将发布

5. 装饰器模式 (Decorator Pattern)

装饰器模式动态地将责任附加到对象上,提供了比继承更有弹性的替代方案。

主要特点

  • 在不改变原有对象的基础上动态地给对象添加功能
  • 通过组合而非继承的方式扩展对象功能
  • 可以动态地添加或删除职责
  • 支持职责的动态组合

适用场景

  • 需要动态添加功能且不影响其他对象
  • 需要添加一些基本功能的排列组合
  • 继承关系过于复杂时的替代方案
  • 需要运行时动态扩展功能的场景

注意事项

  • 避免装饰链过长
  • 注意装饰顺序可能影响结果
  • 注意装饰器与被装饰对象接口的一致性
  • 合理设计装饰器的粒度

代码实现

Typescript
// 定义基础组件接口
interface Coffee {
    cost(): number;
    description(): string;
}

// 基础咖啡类
class SimpleCoffee implements Coffee {
    cost(): number {
        return 10;
    }

    description(): string {
        return "简单咖啡";
    }
}

// 抽象装饰器类
abstract class CoffeeDecorator implements Coffee {
    protected coffee: Coffee;

    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }

    cost(): number {
        return this.coffee.cost();
    }

    description(): string {
        return this.coffee.description();
    }
}

// 具体装饰器:牛奶
class MilkDecorator extends CoffeeDecorator {
    cost(): number {
        return this.coffee.cost() + 2;
    }

    description(): string {
        return this.coffee.description() + " + 牛奶";
    }
}

// 具体装饰器:糖
class SugarDecorator extends CoffeeDecorator {
    cost(): number {
        return this.coffee.cost() + 1;
    }

    description(): string {
        return this.coffee.description() + " + 糖";
    }
}

// 具体装饰器:焦糖
class CaramelDecorator extends CoffeeDecorator {
    cost(): number {
        return this.coffee.cost() + 3;
    }

    description(): string {
        return this.coffee.description() + " + 焦糖";
    }
}

// 使用示例
const simpleCoffee = new SimpleCoffee();
console.log(simpleCoffee.description(), simpleCoffee.cost()); // 简单咖啡 10

let coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new CaramelDecorator(coffee);

console.log(coffee.description(), coffee.cost()); 
// 输出: 简单咖啡 + 牛奶 + 糖 + 焦糖 16

6. 代理模式 (Proxy Pattern)

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介的作用,并可以添加额外的功能。

主要特点

  • 可以控制对目标对象的访问
  • 可以在访问对象时添加额外的逻辑
  • 支持远程代理、虚拟代理、保护代理等多种形式
  • 可以实现延迟加载、访问控制、日志记录等功能

适用场景

  • 需要控制对某个对象的访问权限
  • 需要在访问对象时添加额外的操作(如日志、性能监控)
  • 需要实现延迟加载(如图片懒加载)
  • 需要对远程对象提供本地代理

注意事项

  • 代理模式会在客户端和真实对象之间增加一个中间层,可能会影响性能
  • 代理类和实际对象的接口需要保持一致
  • 注意代理逻辑的复杂度,避免代理类职责过重
  • 区分代理模式和装饰器模式的使用场景

代码实现

Typescript
// 定义接口
interface Image {
    display(): void;
}

// 真实图片类
class RealImage implements Image {
    private filename: string;

    constructor(filename: string) {
        this.filename = filename;
        this.loadFromDisk();
    }

    private loadFromDisk(): void {
        console.log(`加载图片: ${this.filename}`);
    }

    display(): void {
        console.log(`显示图片: ${this.filename}`);
    }
}

// 代理图片类
class ProxyImage implements Image {
    private realImage: RealImage | null = null;
    private filename: string;
    private accessCount: number = 0;

    constructor(filename: string) {
        this.filename = filename;
    }

    display(): void {
        // 懒加载
        if (this.realImage === null) {
            this.realImage = new RealImage(this.filename);
        }
        
        // 添加访问统计
        this.accessCount++;
        console.log(`图片被访问的次数: ${this.accessCount}`);
        
        // 访问控制
        if (this.accessCount <= 3) {
            this.realImage.display();
        } else {
            console.log('访问次数超限,请升级会员');
        }
    }
}

// 使用示例
const image = new ProxyImage("test.jpg");

// 第一次访问,会加载图片
image.display();
// 输出:
// 加载图片: test.jpg
// 图片被访问的次数: 1
// 显示图片: test.jpg

// 第二次访问,使用缓存
image.display();
// 输出:
// 图片被访问的次数: 2
// 显示图片: test.jpg

// 第四次访问,超出限制
image.display();
image.display();
// 输出:
// 图片被访问的次数: 4
// 访问次数超限,请升级会员

7. 适配器模式 (Adapter Pattern)

适配器模式使接口不兼容的类可以一起工作,将一个类的接口转换成客户端期望的另一个接口。

主要特点

  • 将一个接口转换成另一个接口
  • 使原本由于接口不兼容而不能一起工作的类可以协同工作
  • 可以让任何两个没有关联的类一起运行
  • 提供了类之间的兼容性

适用场景

  • 需要使用现有的类,但其接口不符合需求
  • 需要统一多个类的接口
  • 需要复用一些现有的类,但是接口与复用环境要求不一致
  • 需要适配第三方库的接口

注意事项

  • 适配器不应该过多地改变原有对象的功能
  • 注意适配器的性能开销
  • 考虑是否真的需要适配器,有时重构更合适
  • 适配器模式可能会增加系统的复杂性

代码实现

Typescript
// 目标接口(新接口)
interface ModernPayment {
    processPayment(amount: number): void;
}

// 被适配的类(旧接口)
class LegacyPaymentSystem {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    makePayment(dollars: number): void {
        console.log(`${this.name} 处理付款: ${dollars}美元`);
    }

    verifyAccount(accountId: string): boolean {
        console.log(`验证账户: ${accountId}`);
        return true;
    }
}

// 支付适配器
class PaymentAdapter implements ModernPayment {
    private legacySystem: LegacyPaymentSystem;
    private accountId: string;

    constructor(legacySystem: LegacyPaymentSystem, accountId: string) {
        this.legacySystem = legacySystem;
        this.accountId = accountId;
    }

    processPayment(amount: number): void {
        // 添加账户验证逻辑
        if (this.legacySystem.verifyAccount(this.accountId)) {
            // 调用旧系统的支付方法
            this.legacySystem.makePayment(amount);
        } else {
            throw new Error("账户验证失败");
        }
    }
}

// 现代支付处理器
class ModernPaymentProcessor {
    private paymentSystem: ModernPayment;

    constructor(paymentSystem: ModernPayment) {
        this.paymentSystem = paymentSystem;
    }

    executePayment(amount: number): void {
        console.log("使用现代支付处理器");
        this.paymentSystem.processPayment(amount);
    }
}

// 使用示例
const legacySystem = new LegacyPaymentSystem("旧支付系统");
const adapter = new PaymentAdapter(legacySystem, "user123");
const modernProcessor = new ModernPaymentProcessor(adapter);

// 使用现代接口处理支付
modernProcessor.executePayment(100);
// 输出:
// 使用现代支付处理器
// 验证账户: user123
// 旧支付系统 处理付款: 100美元

8. 命令模式 (Command Pattern)

命令模式将请求封装成对象,使得可以用不同的请求参数化其他对象,并且支持请求的排队执行、记录日志、撤销等操作。

主要特点

  • 将请求封装成对象,实现请求发送者和接收者解耦
  • 可以将命令对象存储在队列中
  • 支持可撤销的操作
  • 可以组合命令,创建宏命令

适用场景

  • 需要将请求发送者和接收者解耦
  • 需要支持命令的排队执行
  • 需要支持操作的撤销/重做功能
  • 需要将一组操作组合成宏命令

注意事项

  • 命令的粒度要适中,避免太多的命令类
  • 考虑命令队列的内存占用
  • 在实现撤销操作时要考虑状态的保存
  • 注意命令模式可能带来的性能开销

代码实现

Typescript
// 命令接口
interface Command {
    execute(): void;
    undo(): void;
}

// 接收者:文本编辑器
class TextEditor {
    private content: string = '';

    getContent(): string {
        return this.content;
    }

    insertText(text: string): void {
        this.content += text;
    }

    deleteText(length: number): void {
        this.content = this.content.slice(0, -length);
    }
}

// 具体命令:插入文本
class InsertCommand implements Command {
    private editor: TextEditor;
    private text: string;

    constructor(editor: TextEditor, text: string) {
        this.editor = editor;
        this.text = text;
    }

    execute(): void {
        this.editor.insertText(this.text);
    }

    undo(): void {
        this.editor.deleteText(this.text.length);
    }
}

// 具体命令:删除文本
class DeleteCommand implements Command {
    private editor: TextEditor;
    private deletedText: string = '';
    private length: number;

    constructor(editor: TextEditor, length: number) {
        this.editor = editor;
        this.length = length;
    }

    execute(): void {
        const content = this.editor.getContent();
        this.deletedText = content.slice(-this.length);
        this.editor.deleteText(this.length);
    }

    undo(): void {
        this.editor.insertText(this.deletedText);
    }
}

// 命令调用者:编辑器操作历史
class EditorCommandHistory {
    private commands: Command[] = [];
    private undoneCommands: Command[] = [];

    execute(command: Command): void {
        command.execute();
        this.commands.push(command);
        this.undoneCommands = []; // 清空重做栈
    }

    undo(): void {
        const command = this.commands.pop();
        if (command) {
            command.undo();
            this.undoneCommands.push(command);
        }
    }

    redo(): void {
        const command = this.undoneCommands.pop();
        if (command) {
            command.execute();
            this.commands.push(command);
        }
    }
}

// 使用示例
const editor = new TextEditor();
const history = new EditorCommandHistory();

// 执行插入命令
history.execute(new InsertCommand(editor, "Hello, "));
history.execute(new InsertCommand(editor, "World!"));
console.log(editor.getContent()); // 输出: Hello, World!

// 撤销最后一次操作
history.undo();
console.log(editor.getContent()); // 输出: Hello, 

// 重做操作
history.redo();
console.log(editor.getContent()); // 输出: Hello, World!

// 删除操作
history.execute(new DeleteCommand(editor, 6));
console.log(editor.getContent()); // 输出: Hello,

// 撤销删除
history.undo();
console.log(editor.getContent()); // 输出: Hello, World!

9. 责任链模式 (Chain of Responsibility Pattern)

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

主要特点

  • 将请求和处理分离,请求者不需要知道是谁处理的
  • 可以动态地组织和分配责任
  • 具有良好的扩展性
  • 每个处理者只关注自己能处理的请求

适用场景

  • 有多个对象可以处理同一个请求,具体由哪个对象处理在运行时决定
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  • 需要动态指定一组可以处理请求的对象
  • 实现过滤器或中间件功能

注意事项

  • 避免责任链过长导致的性能问题
  • 注意避免循环引用
  • 合理设置责任链的处理顺序
  • 考虑请求可能不被处理的情况

代码实现

Typescript
// 定义请求类型
interface Request {
    type: 'LEAVE' | 'EXPENSE' | 'SALARY';
    amount: number;
    description: string;
}

// 抽象处理者
abstract class ApprovalHandler {
    protected nextHandler: ApprovalHandler | null = null;
    protected handlerName: string;
    protected approvalLimit: number;

    constructor(name: string, limit: number) {
        this.handlerName = name;
        this.approvalLimit = limit;
    }

    setNext(handler: ApprovalHandler): ApprovalHandler {
        this.nextHandler = handler;
        return handler;
    }

    handle(request: Request): void {
        if (this.canHandle(request)) {
            this.processRequest(request);
        } else if (this.nextHandler) {
            console.log(`${this.handlerName} 无法处理,转交给下一级`);
            this.nextHandler.handle(request);
        } else {
            console.log('没有人能处理该请求');
        }
    }

    protected abstract canHandle(request: Request): boolean;
    protected abstract processRequest(request: Request): void;
}

// 具体处理者:主管
class Supervisor extends ApprovalHandler {
    constructor() {
        super('主管', 1000);
    }

    protected canHandle(request: Request): boolean {
        return request.type === 'LEAVE' || 
               (request.type === 'EXPENSE' && request.amount <= this.approvalLimit);
    }

    protected processRequest(request: Request): void {
        console.log(`主管审批${request.type}: ${request.description}`);
    }
}

// 具体处理者:经理
class Manager extends ApprovalHandler {
    constructor() {
        super('经理', 5000);
    }

    protected canHandle(request: Request): boolean {
        return request.type === 'EXPENSE' && request.amount <= this.approvalLimit;
    }

    protected processRequest(request: Request): void {
        console.log(`经理审批${request.type}: ${request.description}`);
    }
}

// 具体处理者:总监
class Director extends ApprovalHandler {
    constructor() {
        super('总监', 10000);
    }

    protected canHandle(request: Request): boolean {
        return request.type === 'EXPENSE' || request.type === 'SALARY';
    }

    protected processRequest(request: Request): void {
        console.log(`总监审批${request.type}: ${request.description}`);
    }
}

// 使用示例
const supervisor = new Supervisor();
const manager = new Manager();
const director = new Director();

// 设置责任链
supervisor.setNext(manager).setNext(director);

// 测试不同类型的请求
const requests: Request[] = [
    { type: 'LEAVE', amount: 0, description: '请假一天' },
    { type: 'EXPENSE', amount: 3000, description: '购买办公设备' },
    { type: 'EXPENSE', amount: 8000, description: '团建费用' },
    { type: 'SALARY', amount: 20000, description: '薪资调整' }
];

requests.forEach(request => {
    console.log(`\n处理请求: ${request.description}`);
    supervisor.handle(request);
});

// 输出示例:
// 处理请求: 请假一天
// 主管审批LEAVE: 请假一天

// 处理请求: 购买办公设备
// 主管无法处理,转交给下一级
// 经理审批EXPENSE: 购买办公设备

// 处理请求: 团建费用
// 主管无法处理,转交给下一级
// 经理无法处理,转交给下一级
// 总监审批EXPENSE: 团建费用

// 处理请求: 薪资调整
// 主管无法处理,转交给下一级
// 经理无法处理,转交给下一级
// 总监审批SALARY: 薪资调整

10. 迭代器模式 (Iterator Pattern)

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

主要特点

  • 提供统一的遍历接口
  • 简化集合的遍历过程
  • 分离了集合对象的遍历行为
  • 支持不同的遍历算法

适用场景

  • 需要访问一个集合对象的内容而不暴露其内部表示
  • 需要支持对集合对象的多种遍历方式
  • 需要为遍历不同的集合结构提供一个统一的接口
  • 需要在不同的集合类型中使用相同的遍历代码

注意事项

  • 迭代器的设计要考虑遍历的性能
  • 注意避免遍历过程中的集合修改
  • 考虑是否需要支持双向遍历
  • 注意迭代器的状态管理

代码实现

Typescript
// 迭代器接口
interface Iterator<T> {
    hasNext(): boolean;
    next(): T;
    current(): T;
    reset(): void;
}

// 集合接口
interface Collection<T> {
    createIterator(): Iterator<T>;
}

// 具体迭代器实现
class PlaylistIterator implements Iterator<Song> {
    private playlist: Song[];
    private position: number = 0;

    constructor(playlist: Song[]) {
        this.playlist = playlist;
        this.position = 0;
    }

    hasNext(): boolean {
        return this.position < this.playlist.length;
    }

    next(): Song {
        if (this.hasNext()) {
            const song = this.playlist[this.position];
            this.position++;
            return song;
        }
        throw new Error("没有更多歌曲了");
    }

    current(): Song {
        if (this.position >= this.playlist.length) {
            throw new Error("当前位置无效");
        }
        return this.playlist[this.position];
    }

    reset(): void {
        this.position = 0;
    }
}

// 歌曲类
class Song {
    constructor(
        public title: string,
        public artist: string,
        public duration: number
    ) {}

    play(): void {
        console.log(`正在播放: ${this.title} - ${this.artist} (${this.duration}秒)`);
    }
}

// 具体集合:播放列表
class Playlist implements Collection<Song> {
    private songs: Song[] = [];

    addSong(song: Song): void {
        this.songs.push(song);
    }

    removeSong(song: Song): void {
        const index = this.songs.indexOf(song);
        if (index !== -1) {
            this.songs.splice(index, 1);
        }
    }

    createIterator(): Iterator<Song> {
        return new PlaylistIterator(this.songs);
    }
}

// 音乐播放器
class MusicPlayer {
    private playlist: Playlist;
    private iterator: Iterator<Song>;

    constructor(playlist: Playlist) {
        this.playlist = playlist;
        this.iterator = playlist.createIterator();
    }

    playAll(): void {
        this.iterator.reset();
        while (this.iterator.hasNext()) {
            const song = this.iterator.next();
            song.play();
        }
    }

    playNext(): void {
        if (this.iterator.hasNext()) {
            const song = this.iterator.next();
            song.play();
        } else {
            console.log("播放列表已结束");
        }
    }
}

// 使用示例
const playlist = new Playlist();

// 添加歌曲
playlist.addSong(new Song("Shape of You", "Ed Sheeran", 235));
playlist.addSong(new Song("Blinding Lights", "The Weeknd", 200));
playlist.addSong(new Song("Stay", "Justin Bieber", 138));

// 创建播放器
const player = new MusicPlayer(playlist);

// 播放所有歌曲
console.log("播放所有歌曲:");
player.playAll();
// 输出:
// 正在播放: Shape of You - Ed Sheeran (235秒)
// 正在播放: Blinding Lights - The Weeknd (200秒)
// 正在播放: Stay - Justin Bieber (138秒)

// 创建新的迭代器从头播放
const iterator = playlist.createIterator();
console.log("\n使用迭代器逐个播放:");
while (iterator.hasNext()) {
    const song = iterator.next();
    song.play();
}

总结

单例模式:确保一个类只有一个实例 工厂模式:封装对象的创建过程 策略模式:封装可互换的行为 观察者模式:定义对象间的一对多依赖关系 装饰器模式:动态地给对象添加额外的职责 代理模式:为其他对象提供一个代理以控制对这个对象的访问 适配器模式:使不兼容的接口可以一起工作 命令模式:将请求封装成对象 责任链模式:处理请求的对象链 迭代器模式:顺序访问集合对象的元素

2024 © Powered by
hsBlog
|
后台管理