前端常用的设计模式

一、创建型

1、工厂模式

提供一个创建对象的接口,但由子类决定实例化哪个类。用工厂方法代替new操作,根据传入的类型参数动态创建不同的对象实例。

如:简单工厂模式

// 产品接口

class Product {
    use() {}
}

// 具体产品A

class ConcreteProductA extends Product {
    use() {
        console.log('Using ConcreteProductA');
    }
}

// 具体产品B

class ConcreteProductB extends Product {
    use() {
        console.log('Using ConcreteProductB');
    }
}

// 工厂函数

function SimpleFactory(type) {
    switch (type) {
        case 'A':
            return new ConcreteProductA();
        case 'B':
            return new ConcreteProductB();
        default:
            throw new Error('Unknown product type');
    }
}

// 客户端代码

const productA = SimpleFactory('A');
productA.use(); // 输出: Using ConcreteProductA

const productB = SimpleFactory('B');
productB.use(); // 输出: Using ConcreteProductB

2、单例模式

一个类只有一个实例,只有一个访问入口。

如:vuex 和 redux中的store

常用的模块单例模式如下:

// singleton.js

class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }

    someMethod() {
        console.log('Hello World');
    }
}

const instance = new Singleton();
Object.freeze(instance); // 冻结对象,防止修改
export default instance;

// main.js

import singleton from './singleton.js';

singleton.someMethod(); // 输出: Hello World

3、原型模式(prototype)

创建一个新对象,新对象继承原对象的属性和方法。

如:prototype、Object.Create方法创建函数、ES6中class的extends

二、结构型

1、适配器模式

解决类之间接口不兼容问题,通过适配器模式,由一个对象对另一个对象进行封装。

如:整合第三方SDK、封装旧接口、vue的computed

2、代理模式

当请求到达实际的原始对象之前或者之后执行的某种操作。

如:ES6 的 proxy、jQuery.proxy()方法

3、装饰者模式

动态地给某个对象添加一些额外的职责,是一种实现继承的替代方案。

如:ES7 Decorator、core-decorators

对比:

适配器模式: 提供一个不同的接口(如不同版本的插头)

代理模式: 提供一模一样的接口;显示原有功能,但是经过限制之后的

装饰者模式: 扩展功能,原有功能不变且可直接使用

三、行为型

1、迭代器模式

可以遍历数据结构的方法

如:Array.prototype.forEach、jQuery中的$.each()、ES6 Iterator

2、策略模式

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。

适用于有多种算法可以实现同一功能的场景,且这些算法可以互换使用。如:在电商平台中,可以根据不同的促销策略选择不同的折扣计算方式。

3、观察者模式(发布-订阅模式)

在给定对象上定义一个事件侦听器,当该对象执行我们侦听的操作时,执行一些事件。

如原生JS的DOM事件监听,React的useEffect钩子

4、命令模式

将请求或操作封装到一个对象中,从而允许你将请求或操作的发起者与具体执行者解耦。

多个事件请求可以访问一个封装的对象,通过传参不同执行不同的操作。

// 文本编辑器类,作为接收者

class TextEditor {

    constructor() {
        this.text = '';
        this.history = [];
    }

    insertText(text) {
        this.history.push(this.text);
        this.text += text;
        console.log(`Inserted: ${text}`);
    }

    deleteText(length) {
        this.history.push(this.text);
        this.text = this.text.substring(0, this.text.length - length);
        console.log(`Deleted last ${length} characters`);
    }

    undo() {
        if (this.history.length > 0) {
            this.text = this.history.pop();
            console.log('Undo operation');
        } else {
            console.log('No more actions to undo');
        }
    }

    getText() {
        return this.text;
    }
}

// 命令接口

class Command {
    execute() {}
    undo() {}
}

// 插入文本命令实现

class InsertTextCommand extends Command {
    constructor(editor, text) {
        super();
        this.editor = editor;
        this.text = text;
    }

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

    undo() {
        this.editor.undo();
    }
}

// 删除文本命令实现

class DeleteTextCommand extends Command {

    constructor(editor, length) {
        super();
        this.editor = editor;
        this.length = length;
    }

    execute() {
        this.editor.deleteText(this.length);
    }

    undo() {
        this.editor.undo();
    }
}

// 客户端代码

const editor = new TextEditor();
const insertCommand = new InsertTextCommand(editor, 'Hello, World!');
const deleteCommand = new DeleteTextCommand(editor, 6);

// 执行命令

insertCommand.execute(); // 输出: Inserted: Hello, World!
deleteCommand.execute(); // 输出: Deleted last 6 characters

// 撤销操作

deleteCommand.undo(); // 输出: Undo operation
insertCommand.undo(); // 输出: Undo operation

5、状态模式

允许对象在其内部状态发生改变时改变其行为,将状态转换的代码从主要业务逻辑中抽离出来。

如:阅读状态的改变

// 状态接口

class State {
    handle(context) {}
}

// 具体状态:未读状态

class UnreadState extends State {
    handle(context) {
        console.log('Text is unread');
        context.setState(new ReadState());
    }
}

// 具体状态:已读状态

class ReadState extends State {
    handle(context) {
        console.log('Text is read');
        context.setState(new UnreadState());
    }
}

// 上下文类

class TextEditor {
    constructor() {
        this.state = new UnreadState(); // 初始状态为未读
    }

    setState(state) {
        this.state = state;
    }

    toggleReadStatus() {
        this.state.handle(this);
    }
}

// 客户端代码

const editor = new TextEditor();
editor.toggleReadStatus(); // 输出: Text is unread
editor.toggleReadStatus(); // 输出: Text is read
editor.toggleReadStatus(); // 输出: Text is unread

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注