Skip to content

设计模式在实际开发中的应用 ​

单例模式(Singleton Pattern) ​

单例模式是最简单的设计模式之一,它保证一个类仅有一个实例,并提供一个访问它的全局访问点。

应用场景 ​

  • 全局状态管理(如 Vuex、Redux)
  • 全局配置对象
  • 数据库连接池
  • 日志记录器

代码示例 ​

ts
  class Singleton {
    private static instance: Singleton;
    private constructor() { }
    public static getInstance(): Singleton {
      if (!Singleton.instance) {
        Singleton.instance = new Singleton();
      }
      return Singleton.instance;
    }
  }
  // 实际应用示例:配置管理器
  class ConfigManager {
    private static instance: ConfigManager;
    private config: Record<string, any> = {};
    private constructor() { }
    public static getInstance(): ConfigManager {
      if (!ConfigManager.instance) {
        ConfigManager.instance = new ConfigManager();
      }
      return ConfigManager.instance;
    }
    public setConfig(key: string, value: any): void {
      this.config[key] = value;
    }
    public getConfig(key: string): any {
      return this.config[key];
    }
  }
  // 使用示例
  const configManager = ConfigManager.getInstance();
  configManager.setConfig('apiUrl', 'https://api.example.com');
  console.log(configManager.getConfig('apiUrl')); // https://api.example.com

实际应用 ​

在前端开发中,单例模式经常用于管理全局状态或配置。以下是在 Vue 项目中使用单例模式的示例:

js
  // store/index.ts
  export class Store {
    private static instance: Store;
    private state: Record<string, any> = {};
    private constructor() { }
    public static getInstance(): Store {
      if (!Store.instance) {
        Store.instance = new Store();
      }
      return Store.instance;
    }
    public setState(key: string, value: any): void {
      this.state[key] = value;
    }
    public getState(key: string): any {
      return this.state[key];
    }
  }
  // 在组件中使用
  const store = Store.getInstance();

优点 ​

  1. 保证一个类只有一个实例
  2. 提供了对该实例的全局访问点
  3. 节约系统资源

缺点 ​

  1. 单例模式可能隐藏类之间的依赖关系
  2. 违反单一职责原则
  3. 在并发环境下需要特别注意线程安全问题

工厂模式(Factory Pattern) ​

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,而是通过一个共同的接口来指向新创建的对象。

应用场景 ​

  • 当对象的创建逻辑比较复杂时
  • 需要根据不同条件创建不同的对象时
  • 处理大量具有相似属性的小对象时

代码示例 ​

typescript
// 产品接口
interface Button {
    render(): void;
    onClick(): void;
}

// 具体产品
class WindowsButton implements Button {
    render(): void {
        console.log("渲染 Windows 按钮");
    }
    onClick(): void {
        console.log("触发 Windows 按钮点击事件");
    }
}

class MacButton implements Button {
    render(): void {
        console.log("渲染 MacOS 按钮");
    }
    onClick(): void {
        console.log("触发 MacOS 按钮点击事件");
    }
}

// 工厂类
class ButtonFactory {
    createButton(os: string): Button {
        if (os === 'Windows') {
            return new WindowsButton();
        } else if (os === 'Mac') {
            return new MacButton();
        }
        throw new Error('不支持的操作系统');
    }
}

实际应用 ​

在前端开发中,工厂模式常用于创建不同类型的组件或服务。以下是一个实际的应用示例:

typescript
// 表单验证器工厂
interface Validator {
    validate(value: any): boolean;
}

class EmailValidator implements Validator {
    validate(email: string): boolean {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    }
}

class PhoneValidator implements Validator {
    validate(phone: string): boolean {
        return /^1[3-9]\d{9}$/.test(phone);
    }
}

class ValidatorFactory {
    static createValidator(type: string): Validator {
        switch (type) {
            case 'email':
                return new EmailValidator();
            case 'phone':
                return new PhoneValidator();
            default:
                throw new Error('不支持的验证器类型');
        }
    }
}

// 使用示例
const emailValidator = ValidatorFactory.createValidator('email');
console.log(emailValidator.validate('test@example.com')); // true

优点 ​

  1. 封装了对象的创建过程,使得代码更加灵活
  2. 可以轻松扩展新的产品类型
  3. 遵循开闭原则,对扩展开放,对修改关闭

缺点 ​

  1. 需要创建大量的类,可能会使系统变得复杂
  2. 增加了系统的抽象性和理解难度
  3. 难以支持新种类产品的创建

建造者模式(Builder Pattern) ​

建造者模式是一种创建型设计模式,它允许您分步骤创建复杂对象。该模式特别适合当需要创建的对象具有多个可选配置项时。

应用场景 ​

  • 需要创建复杂对象,且对象的创建过程应该独立于其组成部分
  • 需要生成的对象有不同的表示形式
  • 对象的构建过程中需要提供更好的控制

代码示例 ​

typescript
// 产品
class Computer {
    private parts: string[] = [];

    public addPart(part: string): void {
        this.parts.push(part);
    }

    public listParts(): void {
        console.log(`电脑配置: ${this.parts.join(', ')}`);
    }
}

// 建造者接口
interface ComputerBuilder {
    reset(): void;
    buildCPU(): void;
    buildMemory(): void;
    buildStorage(): void;
    buildGraphicsCard(): void;
    getResult(): Computer;
}

// 具体建造者
class GamingComputerBuilder implements ComputerBuilder {
    private computer: Computer = new Computer();

    reset(): void {
        this.computer = new Computer();
    }

    buildCPU(): void {
        this.computer.addPart("Intel i9 处理器");
    }

    buildMemory(): void {
        this.computer.addPart("32GB DDR5 内存");
    }

    buildStorage(): void {
        this.computer.addPart("2TB NVMe SSD");
    }

    buildGraphicsCard(): void {
        this.computer.addPart("RTX 4090 显卡");
    }

    getResult(): Computer {
        return this.computer;
    }
}

// 指挥者
class Director {
    private builder: ComputerBuilder;

    constructor(builder: ComputerBuilder) {
        this.builder = builder;
    }

    public constructGamingComputer(): void {
        this.builder.reset();
        this.builder.buildCPU();
        this.builder.buildMemory();
        this.builder.buildStorage();
        this.builder.buildGraphicsCard();
    }
}

实际应用 ​

在前端开发中,建造者模式常用于创建复杂的表单或配置对象。以下是一个实际的应用示例:

typescript
// 表单构建器
class FormBuilder {
    private form: any = {};

    public reset(): FormBuilder {
        this.form = {};
        return this;
    }

    public setTitle(title: string): FormBuilder {
        this.form.title = title;
        return this;
    }

    public addField(name: string, type: string, required: boolean = false): FormBuilder {
        if (!this.form.fields) {
            this.form.fields = [];
        }
        this.form.fields.push({
            name,
            type,
            required
        });
        return this;
    }

    public addValidation(field: string, rule: string): FormBuilder {
        if (!this.form.validations) {
            this.form.validations = {};
        }
        if (!this.form.validations[field]) {
            this.form.validations[field] = [];
        }
        this.form.validations[field].push(rule);
        return this;
    }

    public build(): any {
        return this.form;
    }
}

// 使用示例
const loginForm = new FormBuilder()
    .setTitle('登录表单')
    .addField('username', 'text', true)
    .addField('password', 'password', true)
    .addValidation('username', 'required')
    .addValidation('username', 'minLength:3')
    .addValidation('password', 'required')
    .addValidation('password', 'pattern:^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$')
    .build();

优点 ​

  1. 可以分步创建复杂对象,并且能够细粒度地控制创建过程
  2. 同样的构建过程可以创建不同的表示
  3. 代码复用性高,易于扩展
  4. 遵循单一职责原则

缺点 ​

  1. 需要创建多个类,增加了代码的复杂度
  2. 对于简单对象来说可能会显得过度设计
  3. 建造者必须足够灵活以应对产品结构的变化

原型模式(Prototype Pattern) ​

原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需使构造函数依赖于具体的类。这种模式特别适用于创建成本较高的对象,或者需要创建大量相似对象的场景。

应用场景 ​

  • 需要创建大量相似对象时
  • 对象的创建成本较高时
  • 需要避免创建一个与产品类层次平行的工厂类层次时
  • 类的实例只能有几个不同状态组合中的一种时

代码示例 ​

typescript
// 原型接口
interface Prototype {
    clone(): Prototype;
}

// 具体原型
class Document implements Prototype {
    private content: string;
    private format: string;
    private images: string[];

    constructor(content: string, format: string, images: string[]) {
        this.content = content;
        this.format = format;
        this.images = images;
    }

    clone(): Document {
        // 创建深拷贝
        const clonedImages = [...this.images];
        return new Document(this.content, this.format, clonedImages);
    }

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

    public setContent(content: string): void {
        this.content = content;
    }
}

// 文档管理器
class DocumentManager {
    private templates: Map<string, Document> = new Map();

    public registerTemplate(name: string, template: Document): void {
        this.templates.set(name, template);
    }

    public createDocument(templateName: string): Document {
        const template = this.templates.get(templateName);
        if (!template) {
            throw new Error('模板不存在');
        }
        return template.clone();
    }
}

实际应用 ​

在前端开发中,原型模式常用于处理复杂对象的克隆或创建大量相似的组件实例。以下是一个实际的应用示例:

typescript
// 组件原型系统
interface ComponentPrototype {
    clone(): ComponentPrototype;
    render(): void;
}

class CardComponent implements ComponentPrototype {
    private title: string;
    private content: string;
    private styles: Record<string, string>;

    constructor(title: string, content: string, styles: Record<string, string>) {
        this.title = title;
        this.content = content;
        this.styles = styles;
    }

    clone(): CardComponent {
        // 深拷贝样式对象
        const clonedStyles = JSON.parse(JSON.stringify(this.styles));
        return new CardComponent(this.title, this.content, clonedStyles);
    }

    render(): void {
        console.log(`渲染卡片:${this.title}`);
    }

    setTitle(title: string): void {
        this.title = title;
    }

    setContent(content: string): void {
        this.content = content;
    }
}

// 使用示例
const cardTemplate = new CardComponent(
    '默认标题',
    '默认内容',
    {
        width: '300px',
        padding: '16px',
        borderRadius: '8px',
        boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
    }
);

// 创建多个卡片实例
const card1 = cardTemplate.clone();
card1.setTitle('新闻卡片');
card1.setContent('这是一条新闻');

const card2 = cardTemplate.clone();
card2.setTitle('产品卡片');
card2.setContent('这是一个产品');

优点 ​

  1. 可以在运行时动态添加或删除产品
  2. 通过克隆来创建对象,避免了复杂的初始化过程
  3. 提供了一种创建预配置对象的简单方式
  4. 减少了子类的数量

缺点 ​

  1. 克隆包含循环引用的复杂对象可能会很困难
  2. 深克隆和浅克隆的处理需要特别注意
  3. 每个原型子类都必须实现克隆方法

适配器模式(Adapter Pattern) ​

适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以协同工作。

应用场景 ​

  • 需要使用现有的类,但其接口与其他代码不兼容时
  • 需要复用一些现有的类,但是不可能修改它们的源代码
  • 需要统一多个类的接口设计时
  • 需要适配第三方库或老旧系统的接口时

代码示例 ​

typescript
// 目标接口
interface Target {
    request(): void;
}

// 需要适配的类
class Adaptee {
    public specificRequest(): void {
        console.log('适配者的特殊请求');
    }
}

// 适配器
class Adapter implements Target {
    private adaptee: Adaptee;

    constructor(adaptee: Adaptee) {
        this.adaptee = adaptee;
    }

    public request(): void {
        console.log('适配器转换开始');
        this.adaptee.specificRequest();
        console.log('适配器转换结束');
    }
}

实际应用 ​

在前端开发中,适配器模式常用于处理不同数据格式的转换或接口的兼容。以下是一个实际的应用示例:

typescript
// 后端API响应接口
interface LegacyAPI {
    getUserData(): {
        id: number;
        userName: string;
        userEmail: string;
    };
}

// 新的用户数据接口
interface UserInfo {
    userId: string;
    name: string;
    email: string;
    displayName: string;
}

// 适配器类
class UserDataAdapter implements UserInfo {
    private legacyData: ReturnType<LegacyAPI['getUserData']>;

    constructor(legacyAPI: LegacyAPI) {
        this.legacyData = legacyAPI.getUserData();
    }

    get userId(): string {
        return String(this.legacyData.id);
    }

    get name(): string {
        return this.legacyData.userName;
    }

    get email(): string {
        return this.legacyData.userEmail;
    }

    get displayName(): string {
        return `${this.legacyData.userName} (${this.legacyData.userEmail})`;
    }
}

// 使用示例
class LegacyUserAPI implements LegacyAPI {
    getUserData() {
        return {
            id: 1,
            userName: "张三",
            userEmail: "zhangsan@example.com"
        };
    }
}

// 客户端代码
const legacyAPI = new LegacyUserAPI();
const userAdapter = new UserDataAdapter(legacyAPI);

console.log(userAdapter.displayName); // 输出: "张三 (zhangsan@example.com)"

另一个实际应用示例:支付系统适配器 ​

typescript
// 支付接口
interface Payment {
    pay(amount: number): void;
    refund(amount: number): void;
}

// 支付宝SDK
class AlipaySdk {
    createOrder(price: number) {
        console.log(`支付宝支付: ${price}元`);
    }

    refundOrder(price: number) {
        console.log(`支付宝退款: ${price}元`);
    }
}

// 微信支付SDK
class WeChatPay {
    pay(price: number, callback: (success: boolean) => void) {
        console.log(`微信支付: ${price}元`);
        callback(true);
    }

    reverse(price: number, callback: (success: boolean) => void) {
        console.log(`微信退款: ${price}元`);
        callback(true);
    }
}

// 支付宝适配器
class AlipayAdapter implements Payment {
    private alipaySDK: AlipaySdk;

    constructor(alipaySDK: AlipaySdk) {
        this.alipaySDK = alipaySDK;
    }

    pay(amount: number): void {
        this.alipaySDK.createOrder(amount);
    }

    refund(amount: number): void {
        this.alipaySDK.refundOrder(amount);
    }
}

// 微信支付适配器
class WeChatPayAdapter implements Payment {
    private wechatPay: WeChatPay;

    constructor(wechatPay: WeChatPay) {
        this.wechatPay = wechatPay;
    }

    pay(amount: number): void {
        this.wechatPay.pay(amount, (success) => {
            if (success) {
                console.log('支付成功');
            }
        });
    }

    refund(amount: number): void {
        this.wechatPay.reverse(amount, (success) => {
            if (success) {
                console.log('退款成功');
            }
        });
    }
}

优点 ​

  1. 将接口转换的过程对客户端透明
  2. 提高了代码的复用性
  3. 增加了类的透明性和灵活性
  4. 将具体的转换实现过程封装在适配器中

缺点 ​

  1. 增加了代码的复杂度
  2. 适配器中的代码可能会增加系统的开销
  3. 有时候可能会创建多个适配器类,导致系统变得凌乱

装饰者模式(Decorator Pattern) ​

装饰者模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

应用场景 ​

  • 需要在运行时动态地给对象添加额外的职责
  • 需要扩展一个类的功能,但不希望使用继承
  • 需要动态地撤销或添加功能
  • 需要保持接口的一致性,但需要增加功能时

代码示例 ​

typescript
// 基础组件接口
interface Component {
    operation(): string;
}

// 具体组件
class ConcreteComponent implements Component {
    public operation(): string {
        return "基础组件";
    }
}

// 装饰器基类
abstract class Decorator implements Component {
    protected component: Component;

    constructor(component: Component) {
        this.component = component;
    }

    public operation(): string {
        return this.component.operation();
    }
}

// 具体装饰器A
class ConcreteDecoratorA extends Decorator {
    public operation(): string {
        return `装饰器A(${super.operation()})`;
    }

    public addedBehavior(): string {
        return "装饰器A的额外行为";
    }
}

// 具体装饰器B
class ConcreteDecoratorB extends Decorator {
    public operation(): string {
        return `装饰器B(${super.operation()})`;
    }

    public addedBehavior(): string {
        return "装饰器B的额外行为";
    }
}

实际应用 ​

在前端开发中,装饰者模式常用于扩展组件功能或添加日志记录等横切关注点。以下是一个实际的应用示例:

typescript
// 基础表单组件接口
interface Form {
    submit(): void;
    validate(): boolean;
}

// 基础表单组件
class BasicForm implements Form {
    submit(): void {
        console.log('提交表单数据');
    }

    validate(): boolean {
        console.log('执行基础验证');
        return true;
    }
}

// 表单装饰器基类
abstract class FormDecorator implements Form {
    protected form: Form;

    constructor(form: Form) {
        this.form = form;
    }

    submit(): void {
        this.form.submit();
    }

    validate(): boolean {
        return this.form.validate();
    }
}

// 验证装饰器
class ValidationDecorator extends FormDecorator {
    validate(): boolean {
        const isBaseValid = super.validate();
        if (!isBaseValid) return false;

        console.log('执行额外的字段验证');
        // 添加自定义验证逻辑
        return true;
    }
}

// 日志装饰器
class LoggingDecorator extends FormDecorator {
    submit(): void {
        console.log('记录表单提交日志');
        super.submit();
        console.log('表单提交完成');
    }
}

// 防重复提交装饰器
class DuplicateSubmitPreventionDecorator extends FormDecorator {
    private isSubmitting = false;

    submit(): void {
        if (this.isSubmitting) {
            console.log('表单正在提交中,请勿重复提交');
            return;
        }

        this.isSubmitting = true;
        try {
            super.submit();
        } finally {
            this.isSubmitting = false;
        }
    }
}

// 使用示例
const form = new BasicForm();
const validatedForm = new ValidationDecorator(form);
const loggingForm = new LoggingDecorator(validatedForm);
const safeForm = new DuplicateSubmitPreventionDecorator(loggingForm);

// 使用装饰后的表单
if (safeForm.validate()) {
    safeForm.submit();
}

另一个实际应用示例:HTTP请求装饰器 ​

typescript
// 基础HTTP客户端接口
interface HttpClient {
    request(url: string, options?: any): Promise<any>;
}

// 基础HTTP客户端
class BasicHttpClient implements HttpClient {
    async request(url: string, options?: any): Promise<any> {
        return fetch(url, options).then(res => res.json());
    }
}

// HTTP客户端装饰器基类
abstract class HttpClientDecorator implements HttpClient {
    protected client: HttpClient;

    constructor(client: HttpClient) {
        this.client = client;
    }

    async request(url: string, options?: any): Promise<any> {
        return this.client.request(url, options);
    }
}

// 缓存装饰器
class CacheDecorator extends HttpClientDecorator {
    private cache = new Map<string, any>();

    async request(url: string, options?: any): Promise<any> {
        const cacheKey = `${url}-${JSON.stringify(options)}`;
        
        if (this.cache.has(cacheKey)) {
            console.log('从缓存获取数据');
            return this.cache.get(cacheKey);
        }

        const response = await super.request(url, options);
        this.cache.set(cacheKey, response);
        return response;
    }
}

// 重试装饰器
class RetryDecorator extends HttpClientDecorator {
    constructor(client: HttpClient, private maxRetries: number = 3) {
        super(client);
    }

    async request(url: string, options?: any): Promise<any> {
        let lastError;
        
        for (let i = 0; i < this.maxRetries; i++) {
            try {
                return await super.request(url, options);
            } catch (error) {
                console.log(`请求失败,第 ${i + 1} 次重试`);
                lastError = error;
            }
        }

        throw lastError;
    }
}

优点 ​

  1. 装饰类和被装饰类可以独立发展,不会相互耦合
  2. 装饰模式是继承的一个替代方案,可以动态扩展对象功能
  3. 可以动态地添加或删除对象的职责
  4. 可以用多个装饰器来装饰同一个对象,得到功能更加强大的对象

缺点 ​

  1. 装饰器模式会增加许多小对象,过度使用会增加程序的复杂度
  2. 装饰器模式比继承更加灵活,但也更加容易出错
  3. 装饰链的顺序很重要,可能会影响最终的结果

观察者模式(Observer Pattern) ​

观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

应用场景 ​

  • 当一个对象的改变需要同时改变其他对象时
  • 当一个对象必须通知其他对象,而它又不知道这些对象是谁时
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时
  • 事件处理系统
  • 消息推送系统

代码示例 ​

typescript
// 观察者接口
interface Observer {
    update(message: string): void;
}

// 主题接口
interface Subject {
    attach(observer: Observer): void;
    detach(observer: Observer): void;
    notify(message: string): void;
}

// 具体主题
class NewsAgency implements Subject {
    private observers: Observer[] = [];

    public attach(observer: Observer): void {
        const isExist = this.observers.includes(observer);
        if (isExist) {
            return console.log('观察者已经被添加过了');
        }
        this.observers.push(observer);
        console.log('观察者已被添加');
    }

    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(message: string): void {
        console.log('通知所有观察者');
        for (const observer of this.observers) {
            observer.update(message);
        }
    }

    public publishNews(news: string): void {
        console.log(`发布新闻: ${news}`);
        this.notify(news);
    }
}

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

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

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

实际应用 ​

在前端开发中,观察者模式常用于处理事件监听和状态管理。以下是一个实际的应用示例:

typescript
// 简单的事件总线实现
class EventBus {
    private static instance: EventBus;
    private subscribers: Map<string, Function[]>;

    private constructor() {
        this.subscribers = new Map();
    }

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

    public subscribe(event: string, callback: Function): void {
        if (!this.subscribers.has(event)) {
            this.subscribers.set(event, []);
        }
        this.subscribers.get(event)?.push(callback);
    }

    public unsubscribe(event: string, callback: Function): void {
        const callbacks = this.subscribers.get(event);
        if (!callbacks) return;

        const index = callbacks.indexOf(callback);
        if (index !== -1) {
            callbacks.splice(index, 1);
        }
    }

    public publish(event: string, data?: any): void {
        const callbacks = this.subscribers.get(event);
        if (!callbacks) return;

        callbacks.forEach(callback => callback(data));
    }
}

// 使用示例
const eventBus = EventBus.getInstance();

// 组件A
class ComponentA {
    constructor() {
        eventBus.subscribe('userUpdated', this.handleUserUpdate.bind(this));
    }

    private handleUserUpdate(userData: any): void {
        console.log('组件A收到用户更新:', userData);
    }
}

// 组件B
class ComponentB {
    constructor() {
        eventBus.subscribe('userUpdated', this.handleUserUpdate.bind(this));
    }

    private handleUserUpdate(userData: any): void {
        console.log('组件B收到用户更新:', userData);
    }
}

// 用户服务
class UserService {
    public updateUser(userData: any): void {
        // 更新用户数据
        console.log('更新用户数据');
        // 通知所有订阅者
        eventBus.publish('userUpdated', userData);
    }
}

// 使用示例
const componentA = new ComponentA();
const componentB = new ComponentB();
const userService = new UserService();

userService.updateUser({ id: 1, name: '张三' });

优点 ​

  1. 实现了观察者和被观察者的解耦
  2. 支持广播通信
  3. 符合开闭原则
  4. 可以在运行时建立对象之间的关系

缺点 ​

  1. 观察者模式可能会引起内存泄漏
  2. 如果观察者太多,通知的过程会花费较多时间
  3. 如果观察者和被观察者之间有循环依赖,可能会导致系统崩溃

策略模式(Strategy Pattern) ​

策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户端而变化。

应用场景 ​

  • 需要在运行时选择算法的场景
  • 有一组类似的算法,并且需要它们能够互相替换
  • 算法的使用会影响到客户端,但你又不想让客户端知道算法的具体实现
  • 一个类定义了多种行为,这些行为在这个类的操作中以多个条件语句的形式出现

代码示例 ​

typescript
// 策略接口
interface PaymentStrategy {
    pay(amount: number): void;
}

// 具体策略
class WeChatPayStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`使用微信支付:${amount}元`);
    }
}

class AlipayStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`使用支付宝支付:${amount}元`);
    }
}

class UnionPayStrategy implements PaymentStrategy {
    pay(amount: number): void {
        console.log(`使用银联支付:${amount}元`);
    }
}

// 上下文
class PaymentContext {
    private strategy: PaymentStrategy;

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

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

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

实际应用 ​

在前端开发中,策略模式常用于表单验证、排序算法选择等场景。以下是一个实际的表单验证应用示例:

typescript
// 验证策略接口
interface ValidationStrategy {
    validate(value: string): boolean;
    getMessage(): string;
}

// 具体验证策略
class RequiredValidation implements ValidationStrategy {
    validate(value: string): boolean {
        return value.length > 0;
    }

    getMessage(): string {
        return '该字段不能为空';
    }
}

class EmailValidation implements ValidationStrategy {
    validate(value: string): boolean {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
    }

    getMessage(): string {
        return '请输入有效的邮箱地址';
    }
}

class PhoneValidation implements ValidationStrategy {
    validate(value: string): boolean {
        return /^1[3-9]\d{9}$/.test(value);
    }

    getMessage(): string {
        return '请输入有效的手机号码';
    }
}

class PasswordValidation implements ValidationStrategy {
    validate(value: string): boolean {
        return /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(value);
    }

    getMessage(): string {
        return '密码必须包含字母和数字,且长度不少于8位';
    }
}

// 表单验证器
class FormValidator {
    private validationStrategies: Map<string, ValidationStrategy[]> = new Map();

    addValidation(field: string, strategy: ValidationStrategy): void {
        if (!this.validationStrategies.has(field)) {
            this.validationStrategies.set(field, []);
        }
        this.validationStrategies.get(field)?.push(strategy);
    }

    validate(field: string, value: string): { isValid: boolean; messages: string[] } {
        const strategies = this.validationStrategies.get(field) || [];
        const messages: string[] = [];

        for (const strategy of strategies) {
            if (!strategy.validate(value)) {
                messages.push(strategy.getMessage());
            }
        }

        return {
            isValid: messages.length === 0,
            messages
        };
    }
}

// 使用示例
const formValidator = new FormValidator();

// 添加验证规则
formValidator.addValidation('email', new RequiredValidation());
formValidator.addValidation('email', new EmailValidation());
formValidator.addValidation('phone', new PhoneValidation());
formValidator.addValidation('password', new PasswordValidation());

// 验证表单字段
const emailResult = formValidator.validate('email', 'test@example.com');
console.log('邮箱验证结果:', emailResult);

const phoneResult = formValidator.validate('phone', '13812345678');
console.log('手机号验证结果:', phoneResult);

const passwordResult = formValidator.validate('password', 'abc123456');
console.log('密码验证结果:', passwordResult);

优点 ​

  1. 算法可以自由切换,避免使用多重条件判断
  2. 遵循开闭原则,易于扩展和维护
  3. 策略类之间可以自由替换
  4. 可以避免使用大量的if-else语句

缺点 ​

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
  2. 策略模式会造成很多的策略类,增加维护难度
  3. 所有策略类都需要对外暴露

命令模式(Command Pattern) ​

命令模式是一种行为设计模式,它将请求封装成对象,从而允许你使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

应用场景 ​

  • 需要将请求发送者和接收者解耦时
  • 需要将请求排队或者记录请求日志时
  • 需要支持撤销/重做操作时
  • 需要支持事务操作时
  • GUI中的菜单、按钮等操作的实现

代码示例 ​

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

// 接收者
class Light {
    private isOn: boolean = false;

    turnOn(): void {
        this.isOn = true;
        console.log('灯已打开');
    }

    turnOff(): void {
        this.isOn = false;
        console.log('灯已关闭');
    }
}

// 具体命令
class TurnOnCommand implements Command {
    constructor(private light: Light) {}

    execute(): void {
        this.light.turnOn();
    }

    undo(): void {
        this.light.turnOff();
    }
}

class TurnOffCommand implements Command {
    constructor(private light: Light) {}

    execute(): void {
        this.light.turnOff();
    }

    undo(): void {
        this.light.turnOn();
    }
}

// 调用者
class RemoteControl {
    private commands: Command[] = [];
    private undoStack: Command[] = [];

    addCommand(command: Command): void {
        this.commands.push(command);
    }

    executeCommand(index: number): void {
        if (index >= 0 && index < this.commands.length) {
            const command = this.commands[index];
            command.execute();
            this.undoStack.push(command);
        }
    }

    undo(): void {
        const command = this.undoStack.pop();
        if (command) {
            command.undo();
        }
    }
}

实际应用 ​

在前端开发中,命令模式常用于实现撤销/重做功能或者操作记录。以下是一个实际的文本编辑器应用示例:

typescript
// 文本编辑器的命令模式实现
interface EditorCommand {
    execute(): void;
    undo(): void;
}

class TextEditor {
    private content: string = '';

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

    insertText(text: string, position: number): void {
        this.content = this.content.slice(0, position) + text + this.content.slice(position);
    }

    deleteText(start: number, end: number): void {
        this.content = this.content.slice(0, start) + this.content.slice(end);
    }
}

class InsertTextCommand implements EditorCommand {
    private oldContent: string;

    constructor(
        private editor: TextEditor,
        private text: string,
        private position: number
    ) {
        this.oldContent = editor.getContent();
    }

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

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

class DeleteTextCommand implements EditorCommand {
    private deletedText: string;
    private oldContent: string;

    constructor(
        private editor: TextEditor,
        private start: number,
        private end: number
    ) {
        this.oldContent = editor.getContent();
        this.deletedText = this.oldContent.slice(start, end);
    }

    execute(): void {
        this.editor.deleteText(this.start, this.end);
    }

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

// 编辑器历史记录管理器
class EditorHistory {
    private commands: EditorCommand[] = [];
    private currentIndex: number = -1;

    execute(command: EditorCommand): void {
        // 执行新命令时,清除当前位置之后的所有命令
        this.commands.splice(this.currentIndex + 1);
        this.commands.push(command);
        command.execute();
        this.currentIndex++;
    }

    undo(): void {
        if (this.currentIndex >= 0) {
            this.commands[this.currentIndex].undo();
            this.currentIndex--;
        }
    }

    redo(): void {
        if (this.currentIndex < this.commands.length - 1) {
            this.currentIndex++;
            this.commands[this.currentIndex].execute();
        }
    }
}

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

// 插入文本
history.execute(new InsertTextCommand(editor, 'Hello', 0));
console.log(editor.getContent()); // 输出: Hello

// 插入更多文本
history.execute(new InsertTextCommand(editor, ' World', 5));
console.log(editor.getContent()); // 输出: Hello World

// 删除文本
history.execute(new DeleteTextCommand(editor, 5, 11));
console.log(editor.getContent()); // 输出: Hello

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

// 重做删除
history.redo();
console.log(editor.getContent()); // 输出: Hello

优点 ​

  1. 降低了系统的耦合度
  2. 新的命令可以很容易地加入到系统中
  3. 可以比较容易地设计一个命令队列和宏命令
  4. 可以方便地实现对请求的撤销和重做

缺点 ​

  1. 可能会导致某些系统有过多的具体命令类
  2. 系统需要支持撤销操作时,命令对象需要保存额外的状态信息
  3. 命令模式的实现可能会变得比较复杂

状态模式(State Pattern) ​

状态模式是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。这种模式将状态封装成独立的类,并将请求委托给当前的状态对象,从而使得对象看起来似乎修改了它的行为。

应用场景 ​

  • 对象的行为取决于它的状态,并且需要在运行时根据状态改变行为
  • 代码中包含大量与对象状态有关的条件语句
  • 需要消除大量的条件分支语句
  • 状态转换规则复杂的场景

代码示例 ​

typescript
// 状态接口
interface State {
    handle(context: Context): void;
}

// 上下文类
class Context {
    private state: State;

    constructor(state: State) {
        this.state = state;
    }

    setState(state: State): void {
        console.log('状态发生改变');
        this.state = state;
    }

    request(): void {
        this.state.handle(this);
    }
}

// 具体状态类
class ConcreteStateA implements State {
    handle(context: Context): void {
        console.log('当前是状态 A');
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB implements State {
    handle(context: Context): void {
        console.log('当前是状态 B');
        context.setState(new ConcreteStateA());
    }
}

实际应用 ​

在前端开发中,状态模式常用于处理复杂的状态转换逻辑。以下是一个订单状态管理的实际应用示例:

typescript
// 订单状态接口
interface OrderState {
    name: string;
    cancelOrder(order: Order): void;
    payOrder(order: Order): void;
    deliverOrder(order: Order): void;
    completeOrder(order: Order): void;
}

// 订单类
class Order {
    private state: OrderState;
    private id: string;

    constructor(id: string) {
        this.id = id;
        // 初始状态为待支付
        this.state = new PendingPaymentState();
    }

    setState(state: OrderState): void {
        console.log(`订单 ${this.id} 状态从 ${this.state.name} 变更为 ${state.name}`);
        this.state = state;
    }

    cancel(): void {
        this.state.cancelOrder(this);
    }

    pay(): void {
        this.state.payOrder(this);
    }

    deliver(): void {
        this.state.deliverOrder(this);
    }

    complete(): void {
        this.state.completeOrder(this);
    }

    getStateName(): string {
        return this.state.name;
    }
}

// 待支付状态
class PendingPaymentState implements OrderState {
    name = '待支付';

    cancelOrder(order: Order): void {
        order.setState(new CancelledState());
    }

    payOrder(order: Order): void {
        order.setState(new PaidState());
    }

    deliverOrder(order: Order): void {
        console.log('订单尚未支付,无法发货');
    }

    completeOrder(order: Order): void {
        console.log('订单尚未支付,无法完成');
    }
}

// 已支付状态
class PaidState implements OrderState {
    name = '已支付';

    cancelOrder(order: Order): void {
        order.setState(new CancelledState());
    }

    payOrder(order: Order): void {
        console.log('订单已支付,无需重复支付');
    }

    deliverOrder(order: Order): void {
        order.setState(new DeliveredState());
    }

    completeOrder(order: Order): void {
        console.log('订单尚未发货,无法完成');
    }
}

// 已发货状态
class DeliveredState implements OrderState {
    name = '已发货';

    cancelOrder(order: Order): void {
        console.log('订单已发货,无法取消');
    }

    payOrder(order: Order): void {
        console.log('订单已支付,无需重复支付');
    }

    deliverOrder(order: Order): void {
        console.log('订单已发货,无需重复发货');
    }

    completeOrder(order: Order): void {
        order.setState(new CompletedState());
    }
}

// 已完成状态
class CompletedState implements OrderState {
    name = '已完成';

    cancelOrder(order: Order): void {
        console.log('订单已完成,无法取消');
    }

    payOrder(order: Order): void {
        console.log('订单已完成,无需支付');
    }

    deliverOrder(order: Order): void {
        console.log('订单已完成,无需发货');
    }

    completeOrder(order: Order): void {
        console.log('订单已完成,无需重复完成');
    }
}

// 已取消状态
class CancelledState implements OrderState {
    name = '已取消';

    cancelOrder(order: Order): void {
        console.log('订单已取消,无需重复取消');
    }

    payOrder(order: Order): void {
        console.log('订单已取消,无法支付');
    }

    deliverOrder(order: Order): void {
        console.log('订单已取消,无法发货');
    }

    completeOrder(order: Order): void {
        console.log('订单已取消,无法完成');
    }
}

// 使用示例
const order = new Order('ORDER_001');
console.log(`订单当前状态: ${order.getStateName()}`);

order.pay();
console.log(`订单当前状态: ${order.getStateName()}`);

order.deliver();
console.log(`订单当前状态: ${order.getStateName()}`);

order.complete();
console.log(`订单当前状态: ${order.getStateName()}`);

优点 ​

  1. 将状态转换规则封装在状态类中,使得状态转换更加明确
  2. 消除了大量的条件判断语句
  3. 状态对象可以被共享
  4. 容易增加新的状态和转换规则

缺点 ​

  1. 状态模式的使用会增加系统中类的数量
  2. 状态模式的结构与实现都较为复杂
  3. 状态模式对"开闭原则"的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码

访问者模式(Visitor Pattern) ​

访问者模式是一种行为设计模式,它允许你在不改变已有类的情况下定义作用于这些类的新操作。访问者模式通过将数据结构和数据操作分离,实现了对修改关闭、对扩展开放的设计原则。

应用场景 ​

  • 需要对一个复杂对象结构中的所有元素执行某些操作
  • 需要在不修改现有类的情况下添加新的操作
  • 数据结构相对稳定,但经常需要在此结构上定义新的操作
  • 需要对不同类型的对象执行不同的操作

代码示例 ​

typescript
// 访问者接口
interface Visitor {
    visitTextElement(element: TextElement): void;
    visitImageElement(element: ImageElement): void;
    visitLinkElement(element: LinkElement): void;
}

// 元素接口
interface Element {
    accept(visitor: Visitor): void;
}

// 具体元素
class TextElement implements Element {
    constructor(private text: string) {}

    getText(): string {
        return this.text;
    }

    accept(visitor: Visitor): void {
        visitor.visitTextElement(this);
    }
}

class ImageElement implements Element {
    constructor(private url: string) {}

    getUrl(): string {
        return this.url;
    }

    accept(visitor: Visitor): void {
        visitor.visitImageElement(this);
    }
}

class LinkElement implements Element {
    constructor(private href: string, private text: string) {}

    getHref(): string {
        return this.href;
    }

    getText(): string {
        return this.text;
    }

    accept(visitor: Visitor): void {
        visitor.visitLinkElement(this);
    }
}

// 具体访问者
class HTMLExportVisitor implements Visitor {
    private output: string = '';

    visitTextElement(element: TextElement): void {
        this.output += `<p>${element.getText()}</p>`;
    }

    visitImageElement(element: ImageElement): void {
        this.output += `<img src="${element.getUrl()}" />`;
    }

    visitLinkElement(element: LinkElement): void {
        this.output += `<a href="${element.getHref()}">${element.getText()}</a>`;
    }

    getOutput(): string {
        return this.output;
    }
}

class MarkdownExportVisitor implements Visitor {
    private output: string = '';

    visitTextElement(element: TextElement): void {
        this.output += `${element.getText()}\n\n`;
    }

    visitImageElement(element: ImageElement): void {
        this.output += `![image](${element.getUrl()})\n`;
    }

    visitLinkElement(element: LinkElement): void {
        this.output += `[${element.getText()}](${element.getHref()})\n`;
    }

    getOutput(): string {
        return this.output;
    }
}

实际应用 ​

在前端开发中,访问者模式常用于处理复杂的文档结构或者实现不同格式的导出功能。以下是一个实际的文档处理应用示例:

typescript
// 文档结构
class Document {
    private elements: Element[] = [];

    addElement(element: Element): void {
        this.elements.push(element);
    }

    accept(visitor: Visitor): void {
        for (const element of this.elements) {
            element.accept(visitor);
        }
    }
}

// 统计访问者
class StatisticsVisitor implements Visitor {
    private textCount = 0;
    private imageCount = 0;
    private linkCount = 0;

    visitTextElement(element: TextElement): void {
        this.textCount++;
    }

    visitImageElement(element: ImageElement): void {
        this.imageCount++;
    }

    visitLinkElement(element: LinkElement): void {
        this.linkCount++;
    }

    getStatistics(): string {
        return `文档统计:\n文本块: ${this.textCount}\n图片: ${this.imageCount}\n链接: ${this.linkCount}`;
    }
}

// 使用示例
const doc = new Document();
doc.addElement(new TextElement('Hello, World!'));
doc.addElement(new ImageElement('image.jpg'));
doc.addElement(new LinkElement('https://example.com', '示例链接'));
doc.addElement(new TextElement('这是第二段文本。'));

// 导出HTML
const htmlVisitor = new HTMLExportVisitor();
doc.accept(htmlVisitor);
console.log('HTML输出:', htmlVisitor.getOutput());

// 导出Markdown
const markdownVisitor = new MarkdownExportVisitor();
doc.accept(markdownVisitor);
console.log('Markdown输出:', markdownVisitor.getOutput());

// 统计文档信息
const statsVisitor = new StatisticsVisitor();
doc.accept(statsVisitor);
console.log(statsVisitor.getStatistics());

优点 ​

  1. 符合单一职责原则,让数据结构和操作分离
  2. 可以在不修改现有代码的情况下添加新的操作
  3. 相关的操作可以集中在一个访问者对象中
  4. 访问者可以在操作对象结构的过程中累积状态

缺点 ​

  1. 增加新的元素类很困难,需要修改所有现有的访问者
  2. 可能会违反封装原则,因为访问者需要访问元素的内部状态
  3. 在需要对元素进行很多不同操作时,维护成本较高

模板方法模式(Template Method Pattern) ​

模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并将一些步骤延迟到子类中实现。模板方法模式使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。

应用场景 ​

  • 需要在多个子类中共享代码
  • 需要在子类中实现算法的某些特定步骤
  • 需要在子类中重写算法的某些步骤
  • 需要在子类中实现算法的某些步骤

代码示例 ​

typescript
// 模板方法接口
interface TemplateMethod {
    execute(): void;
}

// 具体模板方法
class ConcreteTemplateMethod implements TemplateMethod {
    execute(): void {
        console.log('模板方法执行');
    }
}

实际应用 ​

在前端开发中,模板方法模式常用于处理多个子类中共享代码或实现算法的某些特定步骤。以下是一个实际的模板方法应用示例:

typescript
// 模板方法接口
interface TemplateMethod {
    execute(): void;
}

// 具体模板方法
class ConcreteTemplateMethod implements TemplateMethod {
    execute(): void {
        console.log('模板方法执行');
    }
}

优点 ​

  1. 可以在多个子类中共享代码
  2. 可以在子类中实现算法的某些特定步骤
  3. 可以在子类中重写算法的某些步骤
  4. 可以在子类中实现算法的某些步骤

缺点 ​

  1. 可能会导致代码重复
  2. 可能会导致子类难以维护
  3. 可能会导致算法的某些步骤难以重写

中介者模式(Mediator Pattern) ​

中介者模式是一种行为设计模式,它通过封装一系列对象之间的交互,使得这些对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

应用场景 ​

  • 一组对象之间存在复杂的通信方式
  • 对象之间的通信导致了大量的相互依赖
  • 需要集中控制多个对象之间的交互
  • 系统中对象需要频繁地相互通信

代码示例 ​

typescript
// 中介者接口
interface ChatMediator {
    sendMessage(message: string, user: User): void;
    addUser(user: User): void;
}

// 抽象用户类
abstract class User {
    protected mediator: ChatMediator;
    protected name: string;

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

    abstract send(message: string): void;
    abstract receive(message: string): void;
}

// 具体中介者
class ChatRoom implements ChatMediator {
    private users: User[] = [];

    addUser(user: User): void {
        this.users.push(user);
    }

    sendMessage(message: string, sender: User): void {
        for (const user of this.users) {
            // 消息不发送给发送者本人
            if (user !== sender) {
                user.receive(message);
            }
        }
    }
}

// 具体用户类
class ChatUser extends User {
    send(message: string): void {
        console.log(`${this.name} 发送消息: ${message}`);
        this.mediator.sendMessage(message, this);
    }

    receive(message: string): void {
        console.log(`${this.name} 收到消息: ${message}`);
    }
}

实际应用 ​

在前端开发中,中介者模式常用于处理复杂的组件通信。以下是一个实际的表单组件通信应用示例:

typescript
// 表单中介者接口
interface FormMediator {
    notify(sender: FormComponent, event: string, data?: any): void;
}

// 抽象表单组件
abstract class FormComponent {
    protected mediator: FormMediator;
    protected name: string;

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

    abstract update(event: string, data?: any): void;
}

// 具体表单组件
class SelectBox extends FormComponent {
    private options: string[] = [];
    private selectedValue: string = '';

    setOptions(options: string[]): void {
        this.options = options;
    }

    select(value: string): void {
        this.selectedValue = value;
        this.mediator.notify(this, 'select', value);
    }

    update(event: string, data?: any): void {
        if (event === 'disable') {
            console.log(`${this.name} 被禁用`);
        }
    }
}

class CheckBox extends FormComponent {
    private checked: boolean = false;

    toggle(): void {
        this.checked = !this.checked;
        this.mediator.notify(this, 'toggle', this.checked);
    }

    update(event: string, data?: any): void {
        if (event === 'check') {
            this.checked = data;
            console.log(`${this.name} 状态更新为: ${this.checked}`);
        }
    }
}

class TextInput extends FormComponent {
    private value: string = '';

    setValue(value: string): void {
        this.value = value;
        this.mediator.notify(this, 'input', value);
    }

    update(event: string, data?: any): void {
        if (event === 'clear') {
            this.value = '';
            console.log(`${this.name} 已清空`);
        }
    }
}

// 具体中介者
class FormMediator implements FormMediator {
    private components: Map<string, FormComponent> = new Map();

    registerComponent(name: string, component: FormComponent): void {
        this.components.set(name, component);
    }

    notify(sender: FormComponent, event: string, data?: any): void {
        // 处理组件间的联动逻辑
        if (sender instanceof SelectBox && event === 'select') {
            // 当选择框选中特定值时,禁用其他组件
            if (data === 'other') {
                this.components.forEach(component => {
                    if (component !== sender) {
                        component.update('disable');
                    }
                });
            }
        }

        if (sender instanceof CheckBox && event === 'toggle') {
            // 当复选框被选中时,清空输入框
            if (data === true) {
                const input = this.components.get('input');
                if (input) {
                    input.update('clear');
                }
            }
        }
    }
}

// 使用示例
const mediator = new FormMediator();

const select = new SelectBox(mediator, 'select');
const checkbox = new CheckBox(mediator, 'checkbox');
const input = new TextInput(mediator, 'input');

mediator.registerComponent('select', select);
mediator.registerComponent('checkbox', checkbox);
mediator.registerComponent('input', input);

// 测试组件交互
select.select('other');  // 将触发其他组件的禁用
checkbox.toggle();       // 将触发输入框的清空

优点 ​

  1. 减少了对象之间的相互依赖
  2. 集中控制对象之间的交互
  3. 使得对象之间的关系更加清晰
  4. 符合开闭原则,易于扩展

缺点 ​

  1. 中介者对象可能会变得过于复杂
  2. 中介者可能会成为系统的瓶颈
  3. 不当使用可能会导致过度集中化

备忘录模式(Memento Pattern) ​

备忘录模式是一种行为设计模式,它允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。这种模式特别适用于需要实现撤销/重做功能的场景。

应用场景 ​

  • 需要保存对象的历史状态
  • 需要实现撤销/重做功能
  • 直接访问对象的状态会破坏封装性
  • 需要保存对象的快照

代码示例 ​

typescript
// 备忘录类
class Memento {
    constructor(private state: string) {}

    getState(): string {
        return this.state;
    }
}

// 发起人类
class Originator {
    private state: string;

    setState(state: string): void {
        console.log(`设置状态为: ${state}`);
        this.state = state;
    }

    save(): Memento {
        console.log('保存当前状态');
        return new Memento(this.state);
    }

    restore(memento: Memento): void {
        this.state = memento.getState();
        console.log(`恢复到状态: ${this.state}`);
    }
}

// 负责人类
class Caretaker {
    private mementos: Memento[] = [];

    addMemento(memento: Memento): void {
        this.mementos.push(memento);
    }

    getMemento(index: number): Memento {
        return this.mementos[index];
    }
}

实际应用 ​

在前端开发中,备忘录模式常用于实现编辑器的撤销/重做功能。以下是一个实际的文本编辑器应用示例:

typescript
// 文本编辑器状态
class EditorState {
    constructor(
        private content: string,
        private cursorPosition: number,
        private selectedText: string
    ) {}

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

    getCursorPosition(): number {
        return this.cursorPosition;
    }

    getSelectedText(): string {
        return this.selectedText;
    }
}

// 编辑器备忘录
class EditorMemento {
    constructor(private state: EditorState) {}

    getState(): EditorState {
        return this.state;
    }
}

// 文本编辑器
class TextEditor {
    private content: string = '';
    private cursorPosition: number = 0;
    private selectedText: string = '';

    setContent(content: string): void {
        this.content = content;
    }

    setCursorPosition(position: number): void {
        this.cursorPosition = position;
    }

    setSelectedText(text: string): void {
        this.selectedText = text;
    }

    createMemento(): EditorMemento {
        return new EditorMemento(
            new EditorState(
                this.content,
                this.cursorPosition,
                this.selectedText
            )
        );
    }

    restore(memento: EditorMemento): void {
        const state = memento.getState();
        this.content = state.getContent();
        this.cursorPosition = state.getCursorPosition();
        this.selectedText = state.getSelectedText();
    }

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

// 历史记录管理器
class History {
    private mementos: EditorMemento[] = [];
    private currentIndex: number = -1;

    push(memento: EditorMemento): void {
        // 删除当前位置之后的所有状态
        this.mementos.splice(this.currentIndex + 1);
        this.mementos.push(memento);
        this.currentIndex++;
    }

    undo(): EditorMemento | null {
        if (this.currentIndex > 0) {
            this.currentIndex--;
            return this.mementos[this.currentIndex];
        }
        return null;
    }

    redo(): EditorMemento | null {
        if (this.currentIndex < this.mementos.length - 1) {
            this.currentIndex++;
            return this.mementos[this.currentIndex];
        }
        return null;
    }
}

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

// 初始状态
editor.setContent('Hello');
history.push(editor.createMemento());

// 修改内容
editor.setContent('Hello World');
history.push(editor.createMemento());

// 再次修改
editor.setContent('Hello World!');
history.push(editor.createMemento());

// 撤销操作
const undoMemento = history.undo();
if (undoMemento) {
    editor.restore(undoMemento);
    console.log(editor.getContent()); // 输出: Hello World
}

// 重做操作
const redoMemento = history.redo();
if (redoMemento) {
    editor.restore(redoMemento);
    console.log(editor.getContent()); // 输出: Hello World!
}

优点 ​

  1. 提供了一种可以恢复状态的机制
  2. 不会破坏对象的封装性
  3. 简化了发起人对象的实现
  4. 提供了对象状态的快照功能

缺点 ​

  1. 如果需要保存的状态较多,会占用大量内存
  2. 可能需要定期清理存储的状态
  3. 在某些语言中,确保备忘录中的状态不被修改可能比较困难

解释器模式(Interpreter Pattern) ​

解释器模式是一种行为设计模式,它定义了一个语言的语法表示,并定义一个解释器来解释该语言中的句子。这种模式特别适用于需要解析简单语言或者特定格式文本的场景。

应用场景 ​

  • 需要解析和执行简单的领域特定语言(DSL)
  • 需要实现公式或表达式计算器
  • 需要处理特定格式的文本解析
  • 需要实现规则引擎

代码示例 ​

typescript
// 表达式接口
interface Expression {
    interpret(context: Context): number;
}

// 上下文类
class Context {
    private variables: Map<string, number> = new Map();

    setVariable(name: string, value: number): void {
        this.variables.set(name, value);
    }

    getVariable(name: string): number {
        const value = this.variables.get(name);
        return value !== undefined ? value : 0;
    }
}

// 数字表达式
class NumberExpression implements Expression {
    constructor(private number: number) {}

    interpret(context: Context): number {
        return this.number;
    }
}

// 变量表达式
class VariableExpression implements Expression {
    constructor(private name: string) {}

    interpret(context: Context): number {
        return context.getVariable(this.name);
    }
}

// 加法表达式
class AddExpression implements Expression {
    constructor(
        private left: Expression,
        private right: Expression
    ) {}

    interpret(context: Context): number {
        return this.left.interpret(context) + this.right.interpret(context);
    }
}

// 减法表达式
class SubtractExpression implements Expression {
    constructor(
        private left: Expression,
        private right: Expression
    ) {}

    interpret(context: Context): number {
        return this.left.interpret(context) - this.right.interpret(context);
    }
}

实际应用 ​

在前端开发中,解释器模式常用于实现简单的表达式计算器或规则解析器。以下是一个实际的规则解析器应用示例:

typescript
// 规则解释器
interface RuleExpression {
    interpret(context: RuleContext): boolean;
}

// 规则上下文
class RuleContext {
    private facts: Map<string, any> = new Map();

    setFact(name: string, value: any): void {
        this.facts.set(name, value);
    }

    getFact(name: string): any {
        return this.facts.get(name);
    }
}

// 条件表达式
class ConditionExpression implements RuleExpression {
    constructor(
        private factName: string,
        private operator: string,
        private value: any
    ) {}

    interpret(context: RuleContext): boolean {
        const factValue = context.getFact(this.factName);
        
        switch (this.operator) {
            case '==':
                return factValue === this.value;
            case '>':
                return factValue > this.value;
            case '<':
                return factValue < this.value;
            case '>=':
                return factValue >= this.value;
            case '<=':
                return factValue <= this.value;
            default:
                return false;
        }
    }
}

// 与表达式
class AndExpression implements RuleExpression {
    constructor(
        private left: RuleExpression,
        private right: RuleExpression
    ) {}

    interpret(context: RuleContext): boolean {
        return this.left.interpret(context) && this.right.interpret(context);
    }
}

// 或表达式
class OrExpression implements RuleExpression {
    constructor(
        private left: RuleExpression,
        private right: RuleExpression
    ) {}

    interpret(context: RuleContext): boolean {
        return this.left.interpret(context) || this.right.interpret(context);
    }
}

// 使用示例
const context = new RuleContext();
context.setFact('age', 25);
context.setFact('score', 85);

// 构建规则:年龄大于18且分数大于80
const rule = new AndExpression(
    new ConditionExpression('age', '>', 18),
    new ConditionExpression('score', '>', 80)
);

// 解释规则
const result = rule.interpret(context);
console.log('规则判断结果:', result); // true

优点 ​

  1. 易于改变和扩展语法
  2. 实现语法规则很简单
  3. 可以轻松地添加新的解释规则
  4. 增加了灵活性

缺点 ​

  1. 复杂的语法可能会导致类数量急剧增加
  2. 效率问题:解释器模式由于使用了大量的循环和递归,可能在解释复杂句子时效率较低
  3. 维护困难:当语法规则数目太大时,维护会变得困难

享元模式(Flyweight Pattern) ​

享元模式是一种结构型设计模式,它通过共享来有效地支持大量细粒度对象。这种模式通过共享相同的对象来最小化内存使用和对象创建的开销。

应用场景 ​

  • 程序需要生成大量相似的对象
  • 对象的大部分状态都可以共享
  • 需要控制内存消耗
  • 对象的内部状态可以被外部状态取代

代码示例 ​

typescript
// 享元接口
interface Shape {
    draw(x: number, y: number, color: string): void;
}

// 具体享元类
class Circle implements Shape {
    private radius: number = 1;

    constructor(radius: number) {
        this.radius = radius;
    }

    draw(x: number, y: number, color: string): void {
        console.log(`绘制半径为 ${this.radius} 的 ${color} 圆形,位置在 (${x}, ${y})`);
    }
}

// 享元工厂
class ShapeFactory {
    private static circles: Map<number, Circle> = new Map();

    static getCircle(radius: number): Circle {
        let circle = this.circles.get(radius);
        
        if (!circle) {
            circle = new Circle(radius);
            this.circles.set(radius, circle);
            console.log(`创建新的半径为 ${radius} 的圆形`);
        }
        
        return circle;
    }

    static getCircleCount(): number {
        return this.circles.size;
    }
}

实际应用 ​

在前端开发中,享元模式常用于优化大量相似对象的创建和管理。以下是一个实际的文本编辑器中字符渲染的应用示例:

typescript
// 字符享元类
class CharacterFlyweight {
    constructor(
        private char: string,
        private fontFamily: string,
        private fontSize: number,
        private isBold: boolean,
        private isItalic: boolean
    ) {}

    render(x: number, y: number, color: string): void {
        console.log(
            `渲染字符 "${this.char}" 在 (${x}, ${y}),` +
            `字体: ${this.fontFamily},` +
            `大小: ${this.fontSize},` +
            `颜色: ${color},` +
            `粗体: ${this.isBold},` +
            `斜体: ${this.isItalic}`
        );
    }
}

// 享元工厂
class CharacterFactory {
    private static characters: Map<string, CharacterFlyweight> = new Map();

    static getCharacter(
        char: string,
        fontFamily: string,
        fontSize: number,
        isBold: boolean,
        isItalic: boolean
    ): CharacterFlyweight {
        // 创建唯一键
        const key = `${char}-${fontFamily}-${fontSize}-${isBold}-${isItalic}`;
        
        let character = this.characters.get(key);
        
        if (!character) {
            character = new CharacterFlyweight(char, fontFamily, fontSize, isBold, isItalic);
            this.characters.set(key, character);
            console.log(`创建新的字符对象: ${key}`);
        }
        
        return character;
    }

    static getCount(): number {
        return this.characters.size;
    }
}

// 文本编辑器
class TextEditor {
    private characters: {
        flyweight: CharacterFlyweight;
        x: number;
        y: number;
        color: string;
    }[] = [];

    insertCharacter(
        char: string,
        x: number,
        y: number,
        color: string,
        fontFamily: string = 'Arial',
        fontSize: number = 12,
        isBold: boolean = false,
        isItalic: boolean = false
    ): void {
        const flyweight = CharacterFactory.getCharacter(
            char,
            fontFamily,
            fontSize,
            isBold,
            isItalic
        );

        this.characters.push({ flyweight, x, y, color });
    }

    render(): void {
        for (const char of this.characters) {
            char.flyweight.render(char.x, char.y, char.color);
        }
    }
}

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

// 插入一些文本
editor.insertCharacter('H', 0, 0, 'black', 'Arial', 12, true);
editor.insertCharacter('e', 10, 0, 'black', 'Arial', 12, false);
editor.insertCharacter('l', 20, 0, 'black', 'Arial', 12, false);
editor.insertCharacter('l', 30, 0, 'black', 'Arial', 12, false);
editor.insertCharacter('o', 40, 0, 'black', 'Arial', 12, false);

// 渲染文本
editor.render();

// 查看创建的字符对象数量
console.log(`创建的字符对象数量: ${CharacterFactory.getCount()}`); // 应该是4,因为'l'被重用了

优点 ​

  1. 大大减少了系统中对象的数量
  2. 节省了内存空间
  3. 外部状态相对独立,不影响内部状态
  4. 共享对象可以被不同的客户端程序共同使用

缺点 ​

  1. 需要分离内部状态和外部状态,增加了系统的复杂性
  2. 需要额外的时间来传递外部状态
  3. 享元模式使得系统更加复杂,使得程序的逻辑复杂化

责任链模式(Chain of Responsibility Pattern) ​

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

应用场景 ​

  • 有多个对象可以处理同一个请求,具体由哪个对象处理在运行时决定
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  • 需要动态指定一组对象处理请求
  • 需要实现请求的发送者和接收者解耦

代码示例 ​

typescript
// 请求类
class Request {
    constructor(
        private type: string,
        private content: string
    ) {}

    getType(): string {
        return this.type;
    }

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

// 抽象处理者
abstract class Handler {
    protected nextHandler: Handler | null = null;

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

    abstract handle(request: Request): void;

    protected handleNext(request: Request): void {
        if (this.nextHandler) {
            this.nextHandler.handle(request);
        } else {
            console.log('没有处理者可以处理该请求');
        }
    }
}

// 具体处理者
class InfoLogHandler extends Handler {
    handle(request: Request): void {
        if (request.getType() === 'info') {
            console.log(`信息日志: ${request.getContent()}`);
        } else {
            this.handleNext(request);
        }
    }
}

class WarningLogHandler extends Handler {
    handle(request: Request): void {
        if (request.getType() === 'warning') {
            console.log(`警告日志: ${request.getContent()}`);
        } else {
            this.handleNext(request);
        }
    }
}

class ErrorLogHandler extends Handler {
    handle(request: Request): void {
        if (request.getType() === 'error') {
            console.log(`错误日志: ${request.getContent()}`);
        } else {
            this.handleNext(request);
        }
    }
}

实际应用 ​

在前端开发中,责任链模式常用于处理事件传播、中间件处理等场景。以下是一个实际的中间件处理应用示例:

typescript
// 中间件接口
interface Middleware {
    handle(request: any, next: () => Promise<void>): Promise<void>;
}

// 请求处理器
class RequestHandler {
    private middlewares: Middleware[] = [];
    private index: number = 0;

    use(middleware: Middleware): void {
        this.middlewares.push(middleware);
    }

    async process(request: any): Promise<void> {
        const next = async () => {
            if (this.index < this.middlewares.length) {
                const middleware = this.middlewares[this.index++];
                await middleware.handle(request, next);
            }
        };

        this.index = 0;
        await next();
    }
}

// 具体中间件
class AuthMiddleware implements Middleware {
    async handle(request: any, next: () => Promise<void>): Promise<void> {
        console.log('验证用户身份');
        if (request.token) {
            await next();
        } else {
            throw new Error('未授权访问');
        }
    }
}

class LogMiddleware implements Middleware {
    async handle(request: any, next: () => Promise<void>): Promise<void> {
        console.log(`请求开始: ${request.url}`);
        await next();
        console.log(`请求结束: ${request.url}`);
    }
}

class ValidationMiddleware implements Middleware {
    async handle(request: any, next: () => Promise<void>): Promise<void> {
        console.log('验证请求参数');
        if (this.validateRequest(request)) {
            await next();
        } else {
            throw new Error('请求参数无效');
        }
    }

    private validateRequest(request: any): boolean {
        // 实现请求验证逻辑
        return true;
    }
}

// 使用示例
const handler = new RequestHandler();

handler.use(new LogMiddleware());
handler.use(new AuthMiddleware());
handler.use(new ValidationMiddleware());

// 处理请求
const request = {
    url: '/api/data',
    token: 'valid-token'
};

handler.process(request)
    .then(() => console.log('请求处理完成'))
    .catch(error => console.error('请求处理失败:', error.message));

优点 ​

  1. 降低了耦合度,请求发送者和接收者解耦
  2. 简化了对象,对象不需要知道链的结构
  3. 增强了给对象指派职责的灵活性
  4. 增加新的处理者很方便

缺点 ​

  1. 不能保证请求一定会被处理
  2. 可能会形成死循环
  3. 调试和测试较为困难
  4. 可能会影响性能,特别是在链比较长的时候

桥接模式(Bridge Pattern) ​

桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们都可以独立地变化。这种模式通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

应用场景 ​

  • 需要避免抽象和实现之间的永久绑定
  • 抽象和实现都需要通过子类进行扩展
  • 需要在运行时切换不同的实现
  • 需要跨平台的实现

代码示例 ​

typescript
// 实现接口
interface DeviceImplementation {
    isEnabled(): boolean;
    enable(): void;
    disable(): void;
    getVolume(): number;
    setVolume(percent: number): void;
    getChannel(): number;
    setChannel(channel: number): void;
}

// 具体实现
class TV implements DeviceImplementation {
    private on: boolean = false;
    private volume: number = 30;
    private channel: number = 1;

    isEnabled(): boolean {
        return this.on;
    }

    enable(): void {
        this.on = true;
    }

    disable(): void {
        this.on = false;
    }

    getVolume(): number {
        return this.volume;
    }

    setVolume(percent: number): void {
        this.volume = Math.min(100, Math.max(0, percent));
    }

    getChannel(): number {
        return this.channel;
    }

    setChannel(channel: number): void {
        this.channel = channel;
    }
}

class Radio implements DeviceImplementation {
    private on: boolean = false;
    private volume: number = 20;
    private channel: number = 87.5;

    isEnabled(): boolean {
        return this.on;
    }

    enable(): void {
        this.on = true;
    }

    disable(): void {
        this.on = false;
    }

    getVolume(): number {
        return this.volume;
    }

    setVolume(percent: number): void {
        this.volume = Math.min(100, Math.max(0, percent));
    }

    getChannel(): number {
        return this.channel;
    }

    setChannel(channel: number): void {
        this.channel = channel;
    }
}

// 抽象遥控器
abstract class RemoteControl {
    constructor(protected device: DeviceImplementation) {}

    togglePower(): void {
        if (this.device.isEnabled()) {
            this.device.disable();
        } else {
            this.device.enable();
        }
    }

    volumeDown(): void {
        this.device.setVolume(this.device.getVolume() - 10);
    }

    volumeUp(): void {
        this.device.setVolume(this.device.getVolume() + 10);
    }

    channelDown(): void {
        this.device.setChannel(this.device.getChannel() - 1);
    }

    channelUp(): void {
        this.device.setChannel(this.device.getChannel() + 1);
    }
}

// 高级遥控器
class AdvancedRemoteControl extends RemoteControl {
    mute(): void {
        this.device.setVolume(0);
    }
}

// 使用示例
const tv = new TV();
const radio = new Radio();

const tvRemote = new AdvancedRemoteControl(tv);
const radioRemote = new RemoteControl(radio);

// 使用电视遥控器
tvRemote.togglePower();
tvRemote.volumeUp();
tvRemote.channelUp();
tvRemote.mute();

// 使用收音机遥控器
radioRemote.togglePower();
radioRemote.volumeDown();
radioRemote.channelDown();

优点 ​

  1. 实现了抽象和实现的分离,提高了系统的灵活性
  2. 对客户端隐藏了实现细节
  3. 符合开闭原则,方便扩展
  4. 实现细节的修改对客户端透明

缺点 ​

  1. 增加了系统的复杂度
  2. 不容易设计具体的抽象类和实现类
  3. 需要正确识别系统中的两个独立变化的维度

组合模式(Composite Pattern) ​

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表现"整体/部分"层次结构,使得用户对单个对象和组合对象的使用具有一致性。

应用场景 ​

  • 需要表示对象的部分-整体层次结构
  • 希望用户忽略组合对象与单个对象的不同
  • 需要统一处理组合对象和单个对象
  • 处理树形结构数据

代码示例 ​

typescript
// 组件接口
interface FileSystemComponent {
    getName(): string;
    getSize(): number;
    print(indent: string): void;
}

// 文件类(叶子节点)
class File implements FileSystemComponent {
    constructor(
        private name: string,
        private size: number
    ) {}

    getName(): string {
        return this.name;
    }

    getSize(): number {
        return this.size;
    }

    print(indent: string): void {
        console.log(`${indent}📄 ${this.name} (${this.size} bytes)`);
    }
}

// 文件夹类(组合节点)
class Directory implements FileSystemComponent {
    private children: FileSystemComponent[] = [];

    constructor(private name: string) {}

    add(component: FileSystemComponent): void {
        this.children.push(component);
    }

    remove(component: FileSystemComponent): void {
        const index = this.children.indexOf(component);
        if (index !== -1) {
            this.children.splice(index, 1);
        }
    }

    getName(): string {
        return this.name;
    }

    getSize(): number {
        return this.children.reduce((sum, child) => sum + child.getSize(), 0);
    }

    print(indent: string): void {
        console.log(`${indent}📁 ${this.name} (${this.getSize()} bytes)`);
        this.children.forEach(child => child.print(indent + '  '));
    }
}

实际应用 ​

在前端开发中,组合模式常用于实现复杂的UI组件树或者虚拟DOM结构。以下是一个实际的UI组件树应用示例:

typescript
// 组件接口
interface UIComponent {
    render(): string;
    addChild?(child: UIComponent): void;
    removeChild?(child: UIComponent): void;
}

// 简单组件(叶子节点)
class Button implements UIComponent {
    constructor(private text: string) {}

    render(): string {
        return `<button>${this.text}</button>`;
    }
}

class Input implements UIComponent {
    constructor(
        private type: string,
        private placeholder: string
    ) {}

    render(): string {
        return `<input type="${this.type}" placeholder="${this.placeholder}">`;
    }
}

// 容器组件(组合节点)
class Form implements UIComponent {
    private children: UIComponent[] = [];

    addChild(child: UIComponent): void {
        this.children.push(child);
    }

    removeChild(child: UIComponent): void {
        const index = this.children.indexOf(child);
        if (index !== -1) {
            this.children.splice(index, 1);
        }
    }

    render(): string {
        const childrenHtml = this.children.map(child => child.render()).join('\n');
        return `<form>\n${childrenHtml}\n</form>`;
    }
}

class Panel implements UIComponent {
    private children: UIComponent[] = [];

    constructor(private title: string) {}

    addChild(child: UIComponent): void {
        this.children.push(child);
    }

    removeChild(child: UIComponent): void {
        const index = this.children.indexOf(child);
        if (index !== -1) {
            this.children.splice(index, 1);
        }
    }

    render(): string {
        const childrenHtml = this.children.map(child => child.render()).join('\n');
        return `<div class="panel">
            <h2>${this.title}</h2>
            ${childrenHtml}
        </div>`;
    }
}

// 使用示例
const loginForm = new Form();
const userPanel = new Panel('用户信息');

userPanel.addChild(new Input('text', '用户名'));
userPanel.addChild(new Input('password', '密码'));

loginForm.addChild(userPanel);
loginForm.addChild(new Button('登录'));

console.log(loginForm.render());

优点 ​

  1. 简化了客户端代码,客户端可以一致地处理组合对象和单个对象
  2. 使得添加新类型的组件变得容易
  3. 提供了清晰的层次结构
  4. 符合开闭原则

缺点 ​

  1. 在组件的种类很多时,可能会让设计变得更加复杂
  2. 可能会让系统的设计变得更加抽象
  3. 在增加新的组件时需要考虑组件的兼容性

迭代器模式(Iterator Pattern) ​

迭代器模式是一种行为设计模式,它提供一种方法来顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表示。这种模式将遍历的责任从集合对象中分离出来,放到迭代器对象中。

应用场景 ​

  • 需要访问一个集合对象的内容而不暴露其内部表示
  • 需要支持对集合对象的多种遍历方式
  • 需要为遍历不同的集合结构提供一个统一的接口
  • 需要在不同的集合间进行统一的迭代操作

代码示例 ​

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

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

// 具体迭代器
class ArrayIterator<T> implements Iterator<T> {
    private index: number = 0;

    constructor(private collection: T[]) {}

    hasNext(): boolean {
        return this.index < this.collection.length;
    }

    next(): T {
        return this.collection[this.index++];
    }

    current(): T {
        return this.collection[this.index];
    }

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

// 具体集合
class NumberCollection implements Collection<number> {
    private numbers: number[] = [];

    constructor(numbers: number[]) {
        this.numbers = numbers;
    }

    createIterator(): Iterator<number> {
        return new ArrayIterator(this.numbers);
    }
}

实际应用 ​

在前端开发中,迭代器模式常用于处理复杂数据结构的遍历。以下是一个实际的树形结构遍历应用示例:

typescript
// 树节点接口
interface TreeNode {
    value: any;
    children: TreeNode[];
}

// 树遍历器接口
interface TreeIterator {
    hasNext(): boolean;
    next(): TreeNode;
}

// 深度优先遍历器
class DepthFirstIterator implements TreeIterator {
    private stack: TreeNode[] = [];

    constructor(root: TreeNode) {
        this.stack.push(root);
    }

    hasNext(): boolean {
        return this.stack.length > 0;
    }

    next(): TreeNode {
        if (!this.hasNext()) {
            throw new Error('没有更多节点');
        }

        const node = this.stack.pop()!;
        // 将子节点按照相反的顺序压入栈中,确保正确的遍历顺序
        for (let i = node.children.length - 1; i >= 0; i--) {
            this.stack.push(node.children[i]);
        }

        return node;
    }
}

// 广度优先遍历器
class BreadthFirstIterator implements TreeIterator {
    private queue: TreeNode[] = [];

    constructor(root: TreeNode) {
        this.queue.push(root);
    }

    hasNext(): boolean {
        return this.queue.length > 0;
    }

    next(): TreeNode {
        if (!this.hasNext()) {
            throw new Error('没有更多节点');
        }

        const node = this.queue.shift()!;
        // 将所有子节点加入队列
        this.queue.push(...node.children);

        return node;
    }
}

// 树结构
class Tree {
    constructor(private root: TreeNode) {}

    getDepthFirstIterator(): TreeIterator {
        return new DepthFirstIterator(this.root);
    }

    getBreadthFirstIterator(): TreeIterator {
        return new BreadthFirstIterator(this.root);
    }
}

// 使用示例
const tree = new Tree({
    value: 'A',
    children: [
        {
            value: 'B',
            children: [
                { value: 'D', children: [] },
                { value: 'E', children: [] }
            ]
        },
        {
            value: 'C',
            children: [
                { value: 'F', children: [] },
                { value: 'G', children: [] }
            ]
        }
    ]
});

// 深度优先遍历
console.log('深度优先遍历:');
const depthIterator = tree.getDepthFirstIterator();
while (depthIterator.hasNext()) {
    console.log(depthIterator.next().value);
}

// 广度优先遍历
console.log('广度优先遍历:');
const breadthIterator = tree.getBreadthFirstIterator();
while (breadthIterator.hasNext()) {
    console.log(breadthIterator.next().value);
}

优点 ​

  1. 支持以不同的方式遍历一个集合
  2. 简化了集合的接口
  3. 在同一个集合上可以有多个遍历
  4. 在迭代器模式中,增加新的集合类和迭代器类都很方便

缺点 ​

  1. 对于简单的集合,使用迭代器模式可能会过于复杂
  2. 使用迭代器模式可能会增加系统的复杂性
  3. 迭代器模式在某些情况下可能比直接遍历集合性能差

代理模式(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;

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

    display(): void {
        if (this.realImage === null) {
            this.realImage = new RealImage(this.filename);
        }
        this.realImage.display();
    }
}

实际应用 ​

在前端开发中,代理模式常用于实现数据访问控制、缓存、延迟加载等功能。以下是一个实际的数据访问代理应用示例:

typescript
// 数据服务接口
interface DataService {
    getData(key: string): Promise<any>;
    setData(key: string, value: any): Promise<void>;
}

// 实际数据服务
class RealDataService implements DataService {
    async getData(key: string): Promise<any> {
        // 模拟API请求
        console.log(`从API获取数据: ${key}`);
        return new Promise(resolve => {
            setTimeout(() => {
                resolve({ [key]: '数据值' });
            }, 1000);
        });
    }

    async setData(key: string, value: any): Promise<void> {
        // 模拟API请求
        console.log(`向API保存数据: ${key} = ${JSON.stringify(value)}`);
        return new Promise(resolve => {
            setTimeout(resolve, 1000);
        });
    }
}

// 缓存代理
class CachedDataService implements DataService {
    private realService: RealDataService;
    private cache: Map<string, any> = new Map();

    constructor(service: RealDataService) {
        this.realService = service;
    }

    async getData(key: string): Promise<any> {
        if (this.cache.has(key)) {
            console.log(`从缓存获取数据: ${key}`);
            return this.cache.get(key);
        }

        const data = await this.realService.getData(key);
        this.cache.set(key, data);
        return data;
    }

    async setData(key: string, value: any): Promise<void> {
        await this.realService.setData(key, value);
        this.cache.set(key, value);
    }
}

// 保护代理
class ProtectedDataService implements DataService {
    private realService: RealDataService;
    private userRole: string;

    constructor(service: RealDataService, userRole: string) {
        this.realService = service;
        this.userRole = userRole;
    }

    async getData(key: string): Promise<any> {
        if (this.userRole === 'admin' || key.startsWith('public_')) {
            return this.realService.getData(key);
        }
        throw new Error('没有权限访问数据');
    }

    async setData(key: string, value: any): Promise<void> {
        if (this.userRole === 'admin') {
            await this.realService.setData(key, value);
        } else {
            throw new Error('没有权限修改数据');
        }
    }
}

// 使用示例
async function main() {
    const realService = new RealDataService();
    const cachedService = new CachedDataService(realService);
    const protectedService = new ProtectedDataService(realService, 'user');

    // 使用缓存代理
    console.log('第一次获取数据:');
    await cachedService.getData('user_1');
    
    console.log('第二次获取数据:');
    await cachedService.getData('user_1');

    // 使用保护代理
    try {
        console.log('尝试访问受保护的数据:');
        await protectedService.getData('admin_data');
    } catch (error) {
        console.error(error.message);
    }

    try {
        console.log('尝试访问公开数据:');
        await protectedService.getData('public_data');
    } catch (error) {
        console.error(error.message);
    }
}

main();

优点 ​

  1. 代理控制对对象的访问,并可以在访问对象时添加额外的操作
  2. 将对象的使用逻辑与实现逻辑分离
  3. 在不修改目标对象的前提下,提供额外的功能操作
  4. 支持远程代理等特殊应用场景

缺点 ​

  1. 在客户端和目标对象之间增加代理对象,可能会降低系统性能
  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂
  3. 可能会增加系统的复杂度