javascript之常见设计模式

前置知识

构造函数

普通函数

 

在 JavaScript 中,很多时候,你需要避免使用 new 关键字。

单例模式

顾名思义,一个类确保只有一个实例,供全局访问

实现单例模式通常包括以下关键步骤:

  1. 私有构造函数:确保单例类的构造函数不能从外部直接调用,以防止创建多个实例。
  2. 静态实例属性:在类定义中创建一个静态属性来存储单例实例。
  3. 全局访问点:提供一个全局访问点,通常是通过一个静态方法获取单例实例。
  4. 懒加载或饿汉式加载:根据需要确定是立即创建实例(饿汉式)还是首次请求时创建(懒加载)。

实际应用

vuex仓库,闭包

工厂模式

通俗点讲

工厂模式,就像是一个提供各种产品的商店。你告诉店员你想要什么产品,店员就会给你制作或者拿给你,而你并不需要知道这个产品是怎么制作出来的

举例

假设我们正在开发一个电商网站,网站中有多种类型的商品,比如衣服、鞋子和电子产品。每个商品都有不同的属性和价格。我们可以使用工厂模式来创建这些商品对象。

首先,我们可以定义一个抽象的商品类(Product),它包含一些通用的属性和方法:

然后,我们可以定义几个具体的商品类,比如衣服(Clothing)、鞋子(Shoes)和电子产品(Electronics),它们都继承自商品类(Product):

 

接下来,我们可以定义一个工厂类(ProductFactory),它负责根据传入的参数创建不同类型的商品对象:

 

最后,我们可以使用工厂类来创建不同类型的商品对象:

使用工厂模式完整代码

        //  定义抽象类
        class Product {
            constructor(name, price) {
                this.name = name;
                this.price = price;
            }

            getDetails() {
                return `${this.name}的价格是${this.price}`;
            }
        }

        // 定义具体商品类
        class Clothing extends Product {
            constructor(name, price, size) {
                super(name, price);
                this.size = size;
            }
        }

        class Shoes extends Product {
            constructor(name, price, brand) {
                super(name, price);
                this.brand = brand;
            }
        }

        class Electronics extends Product {
            constructor(name, price, warranty) {
                super(name, price);
                this.warranty = warranty;
            }
        }

        // 工厂类
        class ProductFactory {
            static createProduct(type, name, price, extraParam) {
                switch (type) {
                    case 'clothing':
                        return new Clothing(name, price, extraParam);
                    case 'shoes':
                        return new Shoes(name, price, extraParam);
                    case 'electronics':
                        return new Electronics(name, price, extraParam);
                    default:
                        throw new Error('Invalid product type');
                }
            }
        }

        const clothing = ProductFactory.createProduct('clothing', 'T-shirt', 50, 'M');
        console.log(clothing.getDetails()); // 输出:T-shirt的价格是50

        const shoes = ProductFactory.createProduct('shoes', 'Running Shoes', 200, 'Nike');
        console.log(shoes.getDetails()); // 输出:Running Shoes的价格是200

        const electronics = ProductFactory.createProduct('electronics', 'Laptop', 1500, '1 Year Warranty');
        console.log(electronics.getDetails()); // 输出:Laptop的价格是1500

 不使用工厂模式完整代码

// 定义抽象类
class Product {
    constructor(name, price) {
        this.name = name;
        this.price = price;
    }

    getDetails() {
        return `${this.name}的价格是${this.price}`;
    }
}

// 定义具体商品类
class Clothing extends Product {
    constructor(name, price, size) {
        super(name, price);
        this.size = size;
    }
}

class Shoes extends Product {
    constructor(name, price, brand) {
        super(name, price);
        this.brand = brand;
    }
}

class Electronics extends Product {
    constructor(name, price, warranty) {
        super(name, price);
        this.warranty = warranty;
    }
}

// 直接实例化各个具体商品类
const clothing = new Clothing('T-shirt', 50, 'M');
console.log(clothing.getDetails()); // 输出:T-shirt的价格是50

const shoes = new Shoes('Running Shoes', 200, 'Nike');
console.log(shoes.getDetails()); // 输出:Running Shoes的价格是200

const electronics = new Electronics('Laptop', 1500, '1 Year Warranty');
console.log(electronics.getDetails()); // 输出:Laptop的价格是1500

两种方式对比,虽然在代码上看,不使用工厂模式更简单,但是在使用工厂模式时,将对象的创建过程封装在工厂类中,使代码更加模块化和可维护,不使用工厂模式会导致创建对象逻辑分散在各个地方。

建造者模式

它可以让你构建复杂的对象,同时保持代码的可读性和易于维护。这种模式将对象的构造过程与其表示分离,使得同样的构建过程可以创建不同的表示。

使用建造者模式时

       class CarBuilder {
                constructor() {
                    this.car = {};
                }

                setBrand(brand) {
                    this.car.brand = brand;
                    return this;
                }

                setModel(model) {
                    this.car.model = model;
                    return this;
                }

                setColor(color) {
                    this.car.color = color;
                    return this;
                }

                build() {
                    return new Car(this.car);
                }
            }

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

            // 使用建造者模式创建车辆对象
            const builder = new CarBuilder();
            const myCar = builder.setBrand("Toyota").setModel("Camry").setColor("Red").build();
            console.log(myCar); // 输出:{ brand: 'Toyota', model: 'Camry', color: 'Red' }

不使用建造者模式时

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

// 创建车辆对象
const myCar = new Car("Toyota", "Camry", "Red");

 对比两者,建造者模式适合复杂的构建对象处理,大概就是 创建过程封装在一个单独的类中  构建时相互不影响,而简单的构建对象,不建议使用建造者模式,多余

原型模式

它通过复制现有对象来创建新对象,而不是通过实例化类来创建。在JavaScript中,原型模式可以通过原型链实现。

使用原型模式时

       // 定义一个构造函数
            function Person(name, age) {
                this.name = name;
                this.age = age;
            }

            // 为构造函数的原型添加方法
            Person.prototype.sayHello = function () {
                console.log('Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.');
            };

            // 创建一个Person实例
            var person1 = new Person('Alice', 30);

            // 调用实例的方法
            person1.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.

            // 使用原型模式创建另一个Person实例
            var person2 = Object.create(Person.prototype);
            person2.name = 'Bob';
            person2.age = 25;

            // 调用实例的方法
            person2.sayHello(); // 输出: Hello, my name is Bob and I am 25 years old.

不使用原型模式时

// 定义一个构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 为构造函数添加方法
Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.');
};

// 创建一个Person实例
var person1 = new Person('Alice', 30);

// 调用实例的方法
person1.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.

// 创建另一个Person实例
var person2 = new Person('Bob', 25);

// 调用实例的方法
person2.sayHello(); // 输出: Hello, my name is Bob and I am 25 years old.

通俗点讲

原型模式的意义在于允许通过复制现有的对象来创建新的对象,而不是通过调用构造函数来实例化。这种方式有几个显著的优点:

  1. 减少构造函数的调用:通过复制已有对象来创建新对象,可以减少构造函数的调用次数,这在构造函数执行成本较高时尤其有用。
  2. 节省资源:如果类的初始化过程消耗资源较多,例如涉及到大量的内存分配或者其他昂贵的操作,使用原型模式可以避免这些开销,因为它是通过复制已有的对象来创建新对象。
  3. 隐藏创建细节:客户端代码不需要了解对象的创建细节,只需要知道原型对象。这样,即使创建过程复杂,客户端代码也可以简单地通过复制原型来获取新对象。
  4. 动态添加或删除属性:原型模式允许在运行时动态地添加或删除属性,这提供了更大的灵活性。
  5. 支持批量操作:有时候需要创建一系列相似或者相关的对象,原型模式可以方便地通过克隆原型来实现这一点。

总的来说,原型模式提供了一种灵活且高效的对象创建方式,特别适用于那些创建成本高或需要频繁创建的场景。

装饰器模式

允许在不修改原始对象的情况下,动态地给对象添加新的功能。

通俗地说,装饰器模式就像给一个已有的物品(对象)添加一些额外的装饰(功能),使得这个物品变得更加丰富和有用

通俗代码,

不使用装饰器模式

使用装饰器模式,将逻辑分开 实现

适配器模式

用于将一个类的接口转换成客户端期望的另一个接口。它通过创建一个适配器类来实现这种转换,使得原本不兼容的接口可以协同工作。

举个例子,假设我们有一个旧的打印机接口,它只能打印黑白文本,而我们现在需要一个能够打印彩色文本的新打印机。我们可以使用适配器模式来解决这个问题。

 

  // 定义一个旧的打印机接口
        class OldPrinter {
            printText(text) {
                console.log("打印黑白文本: " + text);
            }
        }
        // 定义一个新的打印机接口
        class NewPrinter {
            printColorText(text, color) {
                console.log("打印彩色文本: " + text + ",颜色:" + color);
            }
        }
        //  创建一个适配器类,将旧的打印机接口转换为新的打印机接口
        class PrinterAdapter extends OldPrinter {
            constructor(newPrinter) {
                super();
                this.newPrinter = newPrinter;
            }

            printText(text) {
                this.newPrinter.printColorText(text, "黑色");
            }
        }
        //  最后,我们可以使用适配器类来适配旧的打印机接口,使其能够打印彩色文本
        const oldPrinter = new OldPrinter();
        const newPrinter = new NewPrinter();
        const printerAdapter = new PrinterAdapter(newPrinter);

        oldPrinter.printText("Hello, world!"); // 输出:打印黑白文本: Hello, world!
        printerAdapter.printText("Hello, world!"); // 输出:打印彩色文本: Hello, world!,颜色:黑色

不使用适配器模式

        class OldPrinter {
            printText(text) {
                console.log("打印黑白文本: " + text);
            }

            printColorText(text, color) {
                console.log("打印彩色文本: " + text + ",颜色:" + color);
            }
        }

        const PrinterAdapter = new OldPrinter();
        PrinterAdapter.printText("Hello, world!"); // 输出:打印黑白文本: Hello, world!
        PrinterAdapter.printText("Hello, world!","黑色"); // 输出:打印彩色文本: Hello, world!,颜色:黑色

发布-订阅模式

它允许对象之间进行松耦合的通信。在这种模式中,一个对象(称为发布者)会向多个其他对象(称为订阅者)发送消息,而不需要知道这些订阅者的具体实现细节

通俗地说,发布-订阅模式就像是报纸和读者之间的关系。出版商(发布者)发布了一份报纸,而读者(订阅者)可以订阅这份报纸,并在报纸发布时收到通知。

使用发布-订阅模式

        // 创建一个事件中心对象
        const eventCenter = {
            // 存储订阅者的列表
            subscribers: [],

            // 添加订阅者
            subscribe: function (callback) {
                this.subscribers.push(callback);
            },

            // 移除订阅者
            unsubscribe: function (callback) {
                const index = this.subscribers.indexOf(callback);
                if (index !== -1) {
                    this.subscribers.splice(index, 1);
                }
            },

            // 发布消息给所有订阅者
            publish: function (message) {
                this.subscribers.forEach((callback) => callback(message));
            },
        };

        // 定义一个订阅者函数
        function subscriber1(message) {
            console.log("Subscriber 1 received:", message);
        }

        function subscriber2(message) {
            console.log("Subscriber 2 received:", message);
        }

        // 订阅消息
        eventCenter.subscribe(subscriber1);
        eventCenter.subscribe(subscriber2);

        // 发布消息
        eventCenter.publish("Hello, world!");

// 输出结果:
// Subscriber 1 received: Hello, world!
// Subscriber 2 received: Hello, world!

不使用发布-订阅模式

// 定义一个订阅者函数
function subscriber1(message) {
  console.log("Subscriber 1 received:", message);
}

function subscriber2(message) {
  console.log("Subscriber 2 received:", message);
}

// 定义一个发布消息的函数
function publishMessage(message) {
  // 直接调用订阅者函数
  subscriber1(message);
  subscriber2(message);
}

// 发布消息
publishMessage("Hello, world!");

// 输出结果:
// Subscriber 1 received: Hello, world!
// Subscriber 2 received: Hello, world!

观察者模式

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

通俗地说,观察者模式就像是报纸和读者之间的关系。出版商(发布者)发布了一份报纸,而读者(观察者)可以订阅这份报纸,并在报纸发布时收到通知。

使用观察者模式时

// 创建一个发布者对象
const publisher = {
  // 存储观察者的列表
  observers: [],

  // 添加观察者
  subscribe: function (observer) {
    this.observers.push(observer);
  },

  // 移除观察者
  unsubscribe: function (observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  },

  // 通知所有观察者
  notify: function (message) {
    this.observers.forEach((observer) => observer.update(message));
  },
};

// 定义一个观察者函数
function observer1(message) {
  console.log("Observer 1 received:", message);
}

function observer2(message) {
  console.log("Observer 2 received:", message);
}

// 订阅消息
publisher.subscribe(observer1);
publisher.subscribe(observer2);

// 发布消息
publisher.notify("Hello, world!");

// 输出结果:
// Observer 1 received: Hello, world!
// Observer 2 received: Hello, world!

不使用观察者模式时

// 定义一个订阅者函数
function subscriber1(message) {
  console.log("Subscriber 1 received:", message);
}

function subscriber2(message) {
  console.log("Subscriber 2 received:", message);
}

// 定义一个发布消息的函数
function publishMessage(message) {
  // 直接调用订阅者函数
  subscriber1(message);
  subscriber2(message);
}

// 发布消息
publishMessage("Hello, world!");

// 输出结果:
// Subscriber 1 received: Hello, world!
// Subscriber 2 received: Hello, world!

观察者模式 和 发布订阅模式 区别

观察者模式和发布-订阅模式在设计上有着显著的不同,主要体现在耦合性、角色定义和通信方式上。

首先,从耦合性方面来看:

  • 观察者模式中,观察者和被观察者之间是松耦合的关系,即观察者需要知道被观察者的存在,而被观察者也需要维护一个观察者列表。
  • 发布-订阅模式则具有更低的耦合性,发布者和订阅者不需要相互知道对方的存在,它们通过一个经纪人(Broker)或消息中心进行通信。

其次,考虑角色定义的差异:

  • 观察者模式通常涉及两个主要角色:观察者和被观察者。
  • 发布-订阅模式通常包含三个角色:发布者、订阅者和经纪人(Broker)。

最后,关于通信方式的区别:

  • 观察者模式中,当状态变化时,被观察者会直接通知所有注册的观察者。
  • 发布-订阅模式中,状态变化时,发布者发送消息到消息中心,而订阅者从消息中心接收消息,无需直接与发布者交互。

综上所述,虽然观察者模式和发布-订阅模式都是为了实现对象间的解耦和动态协作,但发布-订阅模式提供了更为松散的耦合方式,使得系统组件之间的依赖关系更加灵活。

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值