JS设计模式之“幽灵工厂” - 抽象工厂模式

news2024/9/23 21:28:22

image.png

image.png

一. 了解带头模范 - 抽象类

fileOf7174.png

JavaScript中并没有原生的抽象类的概念,但可以通过一些方式来模拟实现抽象类的效果。

抽象类是一种不能被直接实例化的类,只能作为其他类的基类使用。它定义了一组抽象方法,子类必须实现这些抽象方法。在其他面向对象语言中,抽象类可以用来提供一些通用的属性和方法,并约定具体子类的实现细节。

创建抽象类

JavaScript中,可以通过以下方式模拟抽象类的效果:

  1. 使用普通类模拟抽象类:

定义一个普通类,并在其中定义抽象方法,即方法只有方法签名而没有具体实现。子类必须继承这个普通类,并实现其中的抽象方法。如果子类没有实现抽象方法,将会抛出异常或提示错误。

class AbstractClass {
  abstractMethod() {
    throw new Error('抽象方法必须被实现');
  }
}

class ConcreteClass extends AbstractClass {
  abstractMethod() {
    console.log('具体子类实现的方法');
  }
}

const instance = new ConcreteClass();
instance.abstractMethod(); // 输出:具体子类实现的方法
  1. 使用ES6的Symbol模拟抽象类(不常用,仅供了解):

使用Symbol定义抽象方法,在子类中使用与抽象方法相同名称的Symbol定义具体实现。

const abstractMethod = Symbol('abstractMethod');

class AbstractClass {
  [abstractMethod]() {
    throw new Error('抽象方法必须被实现');
  }
}

class ConcreteClass extends AbstractClass {
  [abstractMethod]() {
    console.log('具体子类实现的方法');
  }
}

const instance = new ConcreteClass();
instance[abstractMethod](); // 输出:具体子类实现的方法

需要注意的是,这种方式只是通过约定和模式来实现抽象类的效果,并没有在JavaScript语法层面上提供对抽象类的直接支持。

抽象类的作用

抽象类在面向对象编程中有以下几个主要作用:

  1. 提供一种约束和规范: 抽象类可以定义一些抽象方法,子类必须实现这些方法。这样可以约束子类在继承抽象类时必须按照一定的规范来实现这些方法,确保代码的一致性和可维护性。

  2. 封装通用的属性和方法: 抽象类可以包含一些通用的属性和方法,这样子类就可以继承这些属性和方法,避免了重复编写代码。通过继承抽象类,子类可以共享抽象类中已经实现的功能。

  3. 实现多态性: 抽象类可以作为多个具体子类的类型,通过抽象类可以实现多态性的特性。在编程中,可以通过抽象类来声明变量或参数的类型,然后在运行时根据实际的具体子类对象赋值给这些变量或参数实现不同子类对象的统一对待。

  4. 降低耦合度: 抽象类可以作为中间层,将具体实现和调用方进行解耦。调用方可以通过抽象类进行交互,而不必依赖具体的子类。这样可以提高代码的灵活性和可维护性,方便进行扩展和修改。

总结:抽象类在面向对象编程中起到了规范、封装、多态和解耦的作用,使得代码更加灵活、可扩展和可维护。

二. “幽灵工厂” - 抽象工厂模式

fileOf7174.png

fileOf7174.png

了解了抽象类之后,我们下面进入正题,一听到工厂你应该会想到就是用来创建对象的,然而这个抽象工厂可不简单,抽象工厂本身不直接创建具体的对象,而是通过具体的工厂子类来创建一组相关的对象,因此被称为“幽灵工厂”,也可以称为“隐藏工厂”。

JavaScript中,抽象工厂模式也是一种创建对象的设计模式,它提供一个接口用于创建一系列相关对象的家族,而无需指定具体的类。

在传统的面向对象编程语言中,抽象工厂模式通常使用类和继承来实现。但在JavaScript中,由于其动态性和灵活性,可以使用函数和原型继承来实现抽象工厂模式。

JavaScript中,抽象工厂模式通常由以下组件组成:

  1. 抽象工厂函数:由一个函数定义,并负责定义一组抽象产品对象的接口。它可以是一个普通的函数,也可以是一个构造函数。

  2. 具体工厂函数:由一个函数定义,并负责创建具体产品对象,实现了抽象工厂的接口。具体工厂函数通常使用原型继承或对象字面量来实现。

  3. 抽象产品对象:由一个普通对象或原型对象定义,并负责定义产品对象的接口。抽象产品对象通常定义了一组共同的方法或属性。

  4. 具体产品对象:由一个普通对象或原型对象定义,并实现了抽象产品对象的接口。具体产品对象通常是根据抽象产品对象定义的模板创建的具体实例。

抽象工厂模式的主要思路是通过抽象工厂函数定义的接口来创建具体的产品对象,这样可以实现对象的解耦和灵活性。客户端代码只需关注抽象工厂函数和抽象产品对象,从而实现了高度的可扩展性和可维护性。

fileOf7174.png

类图 - 抽象工厂模式

参考以上抽象工厂模式的类图,下面以一个简单的JavaScript抽象工厂模式的示例,让我们有一个初步的了解:

// 定义抽象工厂函数
function AbstractFactory() {}

// 定义抽象产品对象
AbstractFactory.prototype.createProduct = function() {
  throw new Error('This is an abstract method.');
};

// 定义具体工厂函数
function ConcreteFactoryA() {}
ConcreteFactoryA.prototype = Object.create(AbstractFactory.prototype);

// 实现具体工厂的抽象产品创建方法
ConcreteFactoryA.prototype.createProduct = function() {
  return {
    name: 'Product A',
    description: 'This is product A.'
  };
};

// 定义具体工厂函数
function ConcreteFactory() {}
ConcreteFactoryB.prototype = Object.create(AbstractFactory.prototype);

// 实现具体工厂的抽象产品创建方法
ConcreteFactoryB.prototype.createProduct = function() {
  return {
    name: 'Product B',
    description: 'This is product B.'
  };
};

// 使用抽象工厂和具体工厂创建产品对象const factoryA = new ConcreteFactoryA();
const productA = factoryA.createProduct();
console.log(productA);

const factoryB = new ConcreteFactoryB();
const productB = factoryB.createProduct();
console.log(productB);

在此示例中,AbstractFactory函数定义了一个抽象工厂的接口,而具体的工厂函数ConcreteFactoryAConcreteFactoryB别实现了抽象工厂的接口。每个体工厂函数都有一个createProduct方法来创建具体产品对象。客户端代码可以通过具体工厂来创建具体产品对象,而无需与具体产品对象直接交互,从而实现了对象的解耦和灵活性。

三. 深入实现抽象工厂模式

fileOf7174.png

场景介绍

当以汽车为例来介绍JavaScript抽象工厂模式的实现时,我们可以假设有两种类型的汽车,即轿车(sedan)和越野车(SUV)。每种汽车类型都有不同的品牌,例如轿车可以是宝马(BMW)或奥迪(Audi),越野车可以是Jeep福特(Ford)。

我们可以使用抽象工厂模式来实现这个示例:

// 定义抽象工厂函数
function CarFactory() {}

// 定义抽象产品对象
CarFactory.prototype.createCar = function() {
  throw new Error('This is an abstract method.');
};

// 定义轿车工厂函数
function SedanFactory() {}
SedanFactory.prototype = Object.create(CarFactory.prototype);

// 实现轿车工厂的抽象产品创建方法
SedanFactory.prototype.createCar = function(brand) {
  switch (brand) {
    case 'BMW':
      return new BMW();
    case 'Audi':
      return new Audi();
    default:
      throw new Error('Invalid car brand: ' + brand);
  }
};

// 定义越野车工厂函数
function SUVFactory() {}
SUVFactory.prototype = Object.create(CarFactory.prototype);

// 实现越野车工厂的抽象产品创建方法
SUVFactory.prototype.createCar = function(brand) {
  switch (brand) {
    case 'Jeep':
      return new Jeep();
    case 'Ford':
      return new Ford();
    default:
      throw new Error('Invalid car brand: ' + brand);
  }
};

// 定义轿车类
function Sedan(brand) {
  this.brand = brand;
}

Sedan.prototype = function() {
  console('Driving sedan ' + this.brand);
};

// 定义越野车类
function SUV(brand) {
  this.brand = brand;
}

SUV.prototype.drive = function() {
  console.log('Driving SUV ' + this.brand);
};

// 定义具体轿车类
function BMW() {
  Sedan.call(this, 'BMW');
}

BMW.prototype = Object.create(Sedan.prototype);

// 定义具体轿车类
function Audi() {
  Sedan.call(this, 'Audi');
}

Audi.prototype = Object.create(Sedan.prototype);

// 定义具体越野车类
function Jeep() {
  SUV.call(this, 'Jeep');
}

Jeep.prototype = Object.create(SUV.prototype);

// 定义具体越野车类
function Ford() {
  SUV.call(this, 'Ford');
}

Ford.prototype = Object.create(SUV.prototype);

// 使用抽象工厂和具体工厂创建具体产品对象
const sedanFactory = new SedanFactory();
const suvFactory = new SUVFactory();

const sedan1 = sedanFactory.createCar('BMW');
const sedan2 = sedanFactory.createCar('Audi');
const suv1 = suvFactory.createCar('Jeep');
const suv2 = suvFactory.createCar('Ford');

sedan1.drive(); // 输出:Driving sedan BMW
sedan2.drive(); // 输出:Driving sedan Audi
suv1.drive();   // 输出:Driving SUV
suv2.drive();   // 输出:Driving SUV Ford

在此示例中,我们定义了抽象工厂函数CarFactory,它有一个createCar的抽象方法。然后,我们创建了SedanFactorySUVFactory作为具体工厂函数,分别实现了CarFactory的抽象方法,用于创建轿车和越野车对象。

为了具体化每个汽车类型,我们定义了SedanSUV类,并通过继承关系创建了具体轿车类(例如BMWAudi)以及具体越野车类(例如JeepFord)。

最后,我们使用具体工厂函数创建了具体汽车对象,然后使用drive方法演示了它们的行驶功能。

这样,通过抽象工厂模式,我们可以轻松地扩展汽车的类型和品牌,而不会影响现有的客户端代码,并且实现了汽车类型和品牌的解耦

四. 对抽象工厂模式的理解

抽象工厂模式是一种使用工厂对象来创建一系列相关对象的设计模式。它通过提供一个共同的接口来创建一组相关或相互依赖的对象,而不需要指定具体的类

抽象工厂模式的核心思想是将对象的创建与使用分离开来。通过抽象工厂函数定义一个接口,具体工厂函数实现这个接口,并负责创建具体的产品对象。客户端代码只与抽象工厂函数和抽象产品对象进行交互,而不需要了解具体的产品对象是如何创建的。

抽象工厂模式的优点之一是它能够提供灵活的对象创建机制。通过定义不同的具体工厂函数,我们可以根据需求创建不同类型或变种的产品对象,而无需修改原有代码。抽象工厂模式还可以隐藏具体产品对象的实现细节,只提供一个统一的接口给客户端使用。

抽象工厂模式适用于以下情况

  1. 当有多个相关的对象需要创建,并且这些对象之间有一定的约束关系时,可以使用抽象工厂模式统一创建这些对象。

  2. 当希望通过一个统一的接来创建一组相关对象时,可以使用抽象工厂模式。

不过,抽象工厂模式也有一些限制和注意事项

  1. 抽象工厂模式增加了系统的复杂性,因为它引入了多个抽象类和多个具体类。

  2. 当需要新增一种产品时,需要同时修改抽象工厂接口和具体工厂实现,这可能会影响到系统的稳定性

  3. 抽象工厂模式难以支持新类型的产品的变化,因为它的设计侧重于一类产品。

在实际使用抽象工厂模式的过程中,我们需要根据具体的业务需求和系统架构来判断是否适合采用。它可以帮助我们实现代码的解耦和灵活性,但也需要权衡其可能引入的复杂性和代码的维护成本。

五. 工厂模式三部曲对比

如果需要对“简单工厂模式”和“工厂方法模式”详细的了解,请参考本人之前文章学习,如下:

JS设计模式之“神奇的魔术师” - 简单工厂模式

JS设计模式之“名片设计师” - 工厂方法模式

简单来说:简单工厂模式工厂方法模式抽象工厂模式是三种常见的创建型设计模式。它们都用于创建对象,但在实现方式和适用场景上有一些差异。

相同点

  1. 都属于创建型设计模式,旨在解耦对象的和使用。

  2. 都通过一个工厂对象/函数创建对象,从而避免了直接使用new操作符来创建对象。

异同点

fileOf7174.png

设计模式异同点适用场景
简单工厂模式
(Simple Factory Pattern
通过一个一个的工厂类(即简单工厂)来创建所有的产品对象。只需要调用工厂类的静态方法,传入相应的参数即可获取特定类型的对象。简单工厂模式适用于创建数量不多,且对象类型相对简单的情况。
工厂方法模式
(Factory Pattern)
将对象的创建延迟到具体的子类工厂中,具体的子工厂实现这个方法来创建不同类型的对象。每个具体的子类工厂负责创建一种具体的产品对象。工厂方法模式适于需要创建多个类型产品,且每个产品可能有不同的逻辑。
抽象工厂模式
(Abstract Factory Pattern)
抽象工厂定义一个接口,该接口声明了一组可以创建不同类型对象的抽象方法。具体的工厂则实现这个接口,并负责创建一组相关的具体产品对象,通过抽象工厂的方法来创建不同类型的体产品对象。抽象工厂模式适用于需要创建一组相关产品对象,而且这些产品对象彼此之间有一定约束关系。

关系

  1. 简单工厂模式是创建型设计模式的基础,它通过一个共同的工厂类创建所有的产品对象,只需通过传入不同的参数来获得不同类型的对象。它属于静态工厂模式

  2. 工厂方法模式扩展了简单工厂模式,它引入了一个抽象的工厂类具体的工厂子类。具体的工厂子类实现该方法来创建特定类型的产品对象。

  3. 抽象工厂模式在工厂方法模式的基础上进一步扩展,它提供了一种组织一组相关或相互依赖的工厂类的方式。具体的工厂类实现了抽象工厂接口,通过与抽象工厂接口交互,无需直接调用具体的工厂类,从而实现了一种高层次的对象创建。

总结

简单工厂模式是创建型模式的基础,工厂方法模式通过引入抽象工厂类和具体工厂子类来实现对象的可定制性,而抽象工厂模式进一步扩展了工厂方法模式,提供了一种组织一组相关工厂类的方式,用于创建相关或相互依赖的对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【函数模板】函数模板的类型推导

一、类型的自动推导 当函数模板的返回值被指定或与传入的参数的类型一致&#xff0c;那么可以直接调用函数模板&#xff0c;而不需要显式的指定参数。 //函数推导 template<typename T, typename R> T Add(T a, R b) {return a b; }void Test1() {//自动推导int x 1;…

【MySQL】索引使用规则——(覆盖索引,单列索引,联合索引,前缀索引,SQL提示,数据分布影响,查询失效情况)

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

【初出江湖】剖析软件架构发展之路

目录标题 架构发展历程单体架构&#xff08;Monolithic&#xff09;垂直拆分分布式服务微服务架构 SOAESB分布式微服务SOA&#xff0c;ESB&#xff0c;微服务的区别和关系分布式与微服务之间的区别于关系 架构发展历程 单体架构&#xff08;Monolithic&#xff09; 单体应用时…

Django 第十一课 -- ORM - 多表实例

目录 一. 前言 二. 创建模型 三. 插入数据 四. ORM - 添加数据 4.1. 一对多(外键 ForeignKey) 4.2. 多对多(ManyToManyField)&#xff1a;在第三张关系表中新增数据 4.3. 关联管理器(对象调用) 五. ORM 查询 5.1. 一对多 5.2. 一对一 5.3. 多对多 六. 基于双下划线…

eNSP-交换机的广播域和冲突域

一、广播域、冲突域 冲突域&#xff1a;一台电脑发送消息&#xff0c;其他电脑不能发送所影响的范围&#xff0c;就是冲突域。 广播域&#xff1a;一台电脑发送广播&#xff0c;所能广播到的范围。 二、举例 1.总线结构 广播域是整个总线的网络范围&#xff0c;冲突域也是整…

uniapp+vue3+setup返回上一页传参

huilderx 版本 list function getId(data) {// console.log(接收到的参数, data)if (data) {buildObj.value JSON.parse(data);} }onShow((options) > {getBuild() })// 暴露方法 defineExpose({getId })detail import { ref, onMounted, onUnmounted } from vue import…

ElasticSearch和Kibana的安全设置以及https设置

&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;ElasticSearch和Kibana的安全设置以及https设置 &#x1f468;‍&#x1f4bb;本文简述&#xff1a;跟着猿灰灰一起学Java&#xff01; &#x1f468;‍&#x1f4bb;上一篇文章&#xff1a; &#x1f468;‍&#x1f4bb;有任…

3D打印透气钢与传统透气钢的差异

透气钢作为一种集金属强度与透气性能于一体的特殊材料&#xff0c;在注塑模具领域扮演着关键角色&#xff0c;通过有效排除模具内困气&#xff0c;显著提升制品成型质量与生产效率。当前&#xff0c;市场上主流的透气钢产品多源自日本、美国&#xff0c;其高昂成本与技术壁垒限…

【国铁采购平台-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

vue项目安装pnpm和无法加载pnpm,已解决

vue3安装pnpm命令&#xff1a; 1.提升依赖安装速度&#xff1a;npm config set registry https://registry.npmjs.org 2.安装pnpm:npm install -g pnpm 3.安装pnpm依赖&#xff1a;pnpm install 4…windows电脑&#xff0c;无法安装pnpm&#xff0c;pnpm install命令&#xff0…

C++基础知识七

1.对象拷贝时编译器优化 现代编译器为了尽快提高程序的效率&#xff0c;不影响正确性的情况下会尽可能减少一些传参和传参过程中可以省略的拷贝 例子&#xff1a; 先调用f&#xff08;&#xff09;函数&#xff0c;则应该先触发构造函数初始化a&#xff0c;return a时先拷贝a…

机器学习之监督学习(一)线性回归、多项式回归、算法优化[巨详细笔记]

机器学习之监督学习&#xff08;一&#xff09;线性回归、多项式回归、算法优化 1.监督学习定义2.监督学习分类2.1回归 regression2.2 分类 classification 3.线性回归 linear regression3.1 单特征线性回归模块一&#xff1a;梯度下降 3.2 多特征线性回归模块二&#xff1a;正…

CohereForAI更新企业级开源模型 c4ai-command-r-08-2024和c4ai-command-r-plus-08-2024

C4AI Command R 08-2024 是一个 350 亿参数高性能生成模型的研究版本。 Command R 08-2024 是一个大型语言模型&#xff0c;采用开放式权重&#xff0c;针对推理、总结和问题解答等各种用例进行了优化。 Command R 08-2024 具备多语言生成功能&#xff0c;曾在 23 种语言上进行…

nginx平滑升级与回滚

华子目录 升级实验环境准备测试内容准备实验要求实验步骤1.解压包2.检测1.26版本的环境3.make编译4.备份之前的nginx启动脚本5.将1.26中的nginx启动脚本覆盖掉1.24中的6.kill -USR2 旧主进程pid7.kill -WINCH 旧主进程pid 实验测试 回滚1.kill -HUP 旧主进程pid2.kill -WINCH 新…

【MySQL】索引性能分析工具详解——>为sql优化(select)做准备

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

机械学习—零基础学习日志(概率论总笔记1)

概率论的起源 在历史上有明确记载的最早研究随机性的数学家是帕斯卡和费马。帕斯卡就是最早发明机械计算机的那位数学家&#xff0c;他并不是赌徒&#xff0c;但是他有些赌徒朋友&#xff0c;那些人常常玩一种掷骰子游戏&#xff0c;游戏规则是由玩家连续掷4次骰子&#xff0c…

Java | Leetcode Java题解之第378题有序矩阵中第K小的元素

题目&#xff1a; 题解&#xff1a; class Solution {public int kthSmallest(int[][] matrix, int k) {int n matrix.length;int left matrix[0][0];int right matrix[n - 1][n - 1];while (left < right) {int mid left ((right - left) >> 1);if (check(matr…

Python酷库之旅-第三方库Pandas(113)

目录 一、用法精讲 496、pandas.DataFrame.kurtosis方法 496-1、语法 496-2、参数 496-3、功能 496-4、返回值 496-5、说明 496-6、用法 496-6-1、数据准备 496-6-2、代码示例 496-6-3、结果输出 497、pandas.DataFrame.max方法 497-1、语法 497-2、参数 497-3、…

如何从 SD 卡恢复已删除的文件:分步指南

在 SD 卡上查找已删除的文件可能是一项相当艰巨的任务&#xff0c;尤其是当您认为它们已经消失得无影无踪时。然而&#xff0c;希望还是有的&#xff01;现代技术提供了多种有效的方法来恢复这些文件&#xff0c;无论是照片、文档还是其他类型的数据。使用正确的工具和一点耐心…

【初出江湖】大白话解释集中式、分布式、微服务的区别?

目录标题 什么是集中式&#xff1f;什么是分布式&#xff1f;分布式系统的架构一般构成模块分布式的优点分布式的缺点什么是分布式集群&#xff1f; 什么是微服务&#xff1f;微服务和分布式系统有什么主要区别&#xff1f;微服务架构与分布式系统在开发过程中有何不同&#xf…