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的价格是50const shoes = ProductFactory.createProduct('shoes', 'Running Shoes', 200, 'Nike');console.log(shoes.getDetails()); // 输出:Running Shoes的价格是200const 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的价格是50const shoes = new Shoes('Running Shoes', 200, 'Nike');
console.log(shoes.getDetails()); // 输出:Running Shoes的价格是200const 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)。

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/522699.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.8 动态规划 背包问题

一.01背包 46. 携带研究材料(第六期模拟笔试) (kamacoder.com) 代码随想录 (programmercarl.com) 携带研究材料: 时间限制:5.000S 空间限制:128MB 题目描述: 小明是一位科学家,他需要参加一场重要的国际科学大会…

基于textdistance计算文本相似度

textdistance是Python的第三方库,用于计算文本之间的相似度或距离。它提供了30个算法,简单易用。 安装 pip install textdistance# 使用扩展库,提高性能 pip install "textdistance[extras]"使用 import textdistance# 计算编辑…

提醒一下!今年考研的人不要太老实了!!

今年准备计算机考研的同学,别太老实了!别人说什么你就信什么 如果你的工作能力不足以支撑找到一个满意的工作,那我建议再沉淀两年! 很多同学其实有点眼高手低,在计算机专业,低于1w的工作看不上&#xff0…

用一个 Python 脚本实现依次运行其他多个带 argparse 命令行参数的 .py 文件

🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 问题描述:在 Windows 环境中,您希望通过一个 Python 脚本来实现特定的自动化任务,该任务需要依次运行其他多个带 argparse 命令行参数的 .py 文件。您希望找到一种简…

华为设备小型园区网方案(有线+无线+防火墙)

(一)配置有线部分 1.配置LSW2 (1)创建相关vlan [LSW2]vlan batch 10 3000 (2)配置连接LSW1的Eth-Trunk1,透传VLAN 10 3000 [LSW2]int Eth-Trunk 1 [LSW2-Eth-Trunk1]port link-type trunk [LSW2…

安装nexus + 部署私有maven仓库

安装nexus 部署私有maven仓库 文章目录 安装nexus 部署私有maven仓库1.下载2.解压3.修改配置文件4.启动5.访问6.查看默认密码7.创建私库8.修改代码配置文件9.在maven 的setting.xml中配置私库的账号密码10.运行manve 【deploy】命令测试11.maven项目引用私库12. 重新加载mave…

指针进阶(4)看一下这些与指针有关的题你都会做吗?

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话: 知不足而奋进,望远山而前行&am…

智慧公厕的三大特点:信息化、数字化、智慧化

智慧公厕是以物联网、互联网、大数据、云计算等先进技术为支撑,对公共厕所的使用、运营、管理、养护进行全方位高效应用的创新型公厕。它具有三大显著特点:(ZonTree中期)信息化、数字化和智慧化。本文以智慧公厕源头实力厂家广州中…

Vision Transformer结构解析

Vision Transformer结构解析 ViT简介ViT参数量ViT三大模块ViT图像预处理模块——PatchEmbed多层Transformer Encoder模块MLP(FFN)模块 基本的Transformer模块Vision Transformer类的实现Transformer知识点网络结构计算复杂度对比Transformer的参数量和计…

几种电脑提示mfc140.dll丢失的解决方法,以及如何预防mfc140.dll丢失

mfc140.dll真是一个超级关键的动态链接库文件!一旦这个文件不翼而飞,可能会导致一些程序无法顺利运行,甚至给系统带来麻烦。但别担心!遇到mfc140.dll文件丢失的情况,我们有一堆应对措施可以立马施行,确保问…

类和对象-C++运算符重载

#include <iostream> #include <string> using namespace std;class Person { public:Person(int age){m_Agenew int (age);}~Person(){if(m_Age!NULL){delete m_Age;m_AgeNULL;}}//重载 赋值运算符Person& operator (Person &p){//编译器提供深拷贝//m_Ag…

143.和弦是什么?和声是什么?三和弦

内容参考于&#xff1a; 三分钟音乐社 上一个内容&#xff1a;142.音程的构唱练习 和弦的定义&#xff1a; 一个音可以把它称为单音 两个音可以把它称为音程 更多的音&#xff0c;通俗的定义上&#xff0c;三个音或者三个以上的音构成的集体就可以叫做和弦&#xff0c;这些音…