【02问:前端常见的设计模式】

一、策略模式

简单来说就是一个问题匹配多个解决方案,不一定使用哪个,而且后续可能会继续增加或减少方案。主要

目录

一、策略模式

二、单例模式

方法一:使用对象字面量

方法二:使用闭包

三、观察者模式

[方法一:使用 ES6 类](#方法一:使用 ES6 类)

方法二:使用函数和闭包

方法三:使用事件机制

四、工厂模式


解决在多种相似算法存在时,使用条件语句(如if...else)导致的复杂性和难以维护的问题。

例如,购物车满减:

  • 满100减10
  • 满200减20
  • 8折
  • ......

使用策略模式实现:

复制代码
<script>
        // 以闭包的形式把折扣方案保存
        const calcPrice = (function () {
            const sale = {
                '100_10': function (price) { return price -= 10 },
                '200_20': function (price) { return price -= 20 },
                '80%': function (price) { return price *= 0.8 },
            }

            // 计算折扣
            function calcPrice(price, type) {
                if(!sale[type]) return '没有这个折扣'
                return sale[type](price)
            }

            // 添加折扣
            calcPrice.add = function (type, fn) {
                if (sale[type]) return '折扣已经存在';
                sale[type] = fn
            }
            return calcPrice
        })()

        const res = calcPrice(200, '80%');
        console.log(res)
    </script>

二、单例模式

使用闭包是实现单例模式的一种常见方法。通过将类的实例保存在闭包中,并提供一个公共方法来获取该实例,可以确保只有一个实例被创建和访问。

方法一:使用对象字面量

这种方法简单直接,通过对象字面量创建一个单例对象。

复制代码
const singleton = {
    property: 'value',
    method: function() {
        console.log('method called');
    }
};

// 使用
singleton.method(); // 输出: method called

方法二:使用闭包

闭包是一种强大的工具,可以用来创建私有变量和方法。

复制代码
const Singleton = (function() {
    let instance;

    function createInstance() {
        const object = new Object('I am the instance');
        return object;
    }

    return {
        getInstance: function() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // 输出: true

三、观察者模式

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

在前端开发中,观察者模式非常适用于事件处理、数据绑定等场景。以下是实现观察者模式的几种常见方法:

方法一:使用 ES6 类

使用 ES6 的类语法,可以优雅地实现观察者模式。

复制代码
class Subject {
    constructor() {
        this.observers = [];
    }

    addObserver(observer) {
        this.observers.push(observer);
    }

    removeObserver(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notifyObservers(data) {
        this.observers.forEach(observer => observer.update(data));
    }
}

class Observer {
    update(data) {
        console.log('Observer received data:', data);
    }
}

// 使用
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers('some data'); // 所有观察者都会收到更新

方法二:使用函数和闭包

如果不使用类,也可以通过函数和闭包来实现观察者模式。

复制代码
function createSubject() {
    let observers = [];

    function addObserver(observer) {
        observers.push(observer);
    }

    function removeObserver(observer) {
        observers = observers.filter(obs => obs !== observer);
    }

    function notifyObservers(data) {
        observers.forEach(observer => observer.update(data));
    }

    return {
        addObserver,
        removeObserver,
        notifyObservers
    };
}

function createObserver() {
    return {
        update(data) {
            console.log('Observer received data:', data);
        }
    };
}

// 使用
const subject = createSubject();
const observer1 = createObserver();
const observer2 = createObserver();

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers('some data'); // 所有观察者都会收到更新

方法三:使用事件机制

前端的事件机制本身就很适合实现观察者模式,例如可以使用自定义事件。

复制代码
class EventEmitter {
    constructor() {
        this.events = {};
    }

    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
    }

    off(event, listener) {
        if (!this.events[event]) return;
        this.events[event] = this.events[event].filter(l => l !== listener);
    }

    emit(event, data) {
        if (!this.events[event]) return;
        this.events[event].forEach(listener => listener(data));
    }
}

// 使用
const emitter = new EventEmitter();

function listener1(data) {
    console.log('Listener 1 received:', data);
}

function listener2(data) {
    console.log('Listener 2 received:', data);
}

emitter.on('event', listener1);
emitter.on('event', listener2);

emitter.emit('event', 'some data'); // 所有监听器都会收到更新

emitter.off('event', listener1);
emitter.emit('event', 'some other data'); // 只有Listener 2 会收到更新

四、工厂模式

工厂模式(Factory Pattern)提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离,而无需指定要创建的具体类。

通过使用工厂模式,可以将对象的创建逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象,这样可以提高代码的可维护性和可扩展性。

复制代码
class VehicleFactory {
    static createVehicle(type, model) {
        switch(type) {
            case 'car':
                return new Car(model);
            case 'truck':
                return new Truck(model);
            default:
                throw new Error('Unknown vehicle type');
        }
    }
}

class Car {
    constructor(model) {
        this.model = model;
    }

    drive() {
        console.log(`${this.model} car is driving`);
    }
}

class Truck {
    constructor(model) {
        this.model = model;
    }

    drive() {
        console.log(`${this.model} truck is driving`);
    }
}

// 使用
const myCar = VehicleFactory.createVehicle('car', 'Toyota');
const myTruck = VehicleFactory.createVehicle('truck', 'Ford');

myCar.drive(); // 输出: Toyota car is driving
myTruck.drive(); // 输出: Ford truck is driving
相关推荐
ssshooter23 分钟前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友27 分钟前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry1 小时前
Jetpack Compose 中的状态
前端
dae bal2 小时前
关于RSA和AES加密
前端·vue.js
柳杉2 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog2 小时前
低端设备加载webp ANR
前端·算法
LKAI.2 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
刺客-Andy3 小时前
React 第七十节 Router中matchRoutes的使用详解及注意事项
前端·javascript·react.js
前端工作日常3 小时前
我对eslint的进一步学习
前端·eslint
禁止摆烂_才浅4 小时前
VsCode 概览尺、装订线、代码块高亮设置
前端·visual studio code