首页 > 其他分享 >设计模式创建型之工厂模式

设计模式创建型之工厂模式

时间:2024-02-23 18:14:27浏览次数:20  
标签:name 创建 age js sex job 工厂 设计模式 class

基本概念

在给出工厂模式的定义之前,不妨先来了解一下工厂的概念。

通过百度百科查到的所谓工厂的定义:是一类用以生产货物的大型工业建筑物,即我们为工厂输送原料,经过工厂对原料进行处理加工之后会输出产物。

例如下面这样一个例子:

张三是一名大学生,毕业后为了上班方便就考虑买一台车,因此他找到一家汽车商城,对工作人员提出来品牌,颜色,价格等需求,随后汽车商城会根据张三的需求做一系列的工作。而对于张三而言,这些都是无需他考虑的,他仅仅提出需求,然后等待结果就好。

这实际上就是工厂的作用,你需要一辆汽车,可以直接从商城里提车,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。

而工厂模式也是如此,在创建对象时不会对调用者暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

在下面的内容中,会带大家了解两种工厂模式:简单工厂模式和抽象工厂模式。

简单工厂模式

简单工厂模式又称为静态工厂方法模式,通过这一模式,我们可以分离构造过程中“变的部分”与“不变的部分”。

即对“变的部分”进行抽离封装,而专注“不变的部分”的改变。

在一所学校里,一般至少存在三种角色:校长,老师,学生。那么在前端里面我们就需要构建三个构造器(通过 ES6 中的类来实现),现在来分别构建这三种角色:

首先,我们在实验环境中新建 index.html 文件与 index.js 文件,并在 index.html 中引入 index.js ,就像这样:

图片描述

随后,在 index.js 中添加校长、老师、学生这三种构造器:

// index.js

// 校长构造器
class Headmaster {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.career = "headmaster";
  }

  job() {
    return "管理学校";
  }
}

// 老师构造器
class Teacher {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.career = "teacher";
  }

  job() {
    return "传授知识";
  }
}

// 学生构造器
class Student {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.career = "student";
  }

  job() {
    return "学习知识";
  }
}

随后在 index.js 中测试这三个构造器是否成功:(如何运行可以参照前言中的运行步骤)

// index.js

const zhangsan = new Headmaster("张三", 36, "man");
console.log("张三的任务是:", zhangsan.job()); // 张三的任务是: 管理学校

const luoxiang = new Teacher("罗翔", 24, "man");
console.log("罗翔的任务是:", luoxiang.job()); // 罗翔的任务是: 传授知识

const xiaohong = new Student("小红", 12, "woman");
console.log("小红的任务是:", xiaohong.job()); // 小红的任务是: 学习知识

可以看到,三个构造器都成功的返回了正确的任务。

在上面的构造器中,除了名字,年龄,性别这三个公共属性之外,还可以根据职业这个属性来分配不同的工作内容。

例如职业是校长(headmaster),那么他/她的工作就是管理学校,职业是老师(teacher),那么其工作就是传授知识。

学校开办一段时间之后,为了改善学校的卫生情况,校长决定增加清洁工这个职务,负责管理校园的清洁卫生,此时我们就需要再增加一个构造器:

// index.js

// 清洁工构造器
class Cleaner {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.career = "cleaner";
  }

  job() {
    return "打扫卫生";
  }
}

同时来验证下其正确性:

// index.js

const lisi = new Cleaner("李四", 60, "man");
console.log("李四的任务是:", lisi.job()); // 李四的任务是: 打扫卫生

不出意外,同样返回了正确的工作任务。

此时,我们很轻松的就添加了一个清洁工构造器,如果你认为这就万事大吉那就错了。随着学校的学生越来越多,学校规模也会越来越大,因此不得不增加生活老师职位负责学生的生活方面,增加食堂师傅负责日常饮食,增加保安负责校园安全等等。

可以看到,在真实情况下的学校里,各种各样的职位非常多,难道我们都需要逐一的去添加相关的构造函数吗?

即使在极端情况下,我们真的添加了这么多构造器,我们在增加的时候不仅需要根据不同的职务去修改他们的工作内容,同时还要考虑职位对应的工作变动的情况,例如当保安人手不够时,需要生活老师去临时参加巡逻工作。这些都让我们不得不去频繁的改动构造器,这样的代价和实现方式显然不能让我们满意。

如何我们能只通过一个构造器就能完成上述工作,让我们回忆下前面提到的对简单工厂模式作用的描述:可以分离构造过程中“变的部分”与“不变的部分”。

那么是否能分离出上面大量构造器的共性呢?当然没问题,请思考一下,这些构造器中“不变的部分”,相信聪明的大家一定已经明白过来,就是其中的名字,年龄与性别这三个属性。

index.js 中继续。

第一步,用一个构造器来实现不同职务的工作分配:

// index.js

// 分配任务
class AssigningTask {
  constructor() {}

  // 静态方法:根据职务分配工作
  static JobAssigning = (name, age, sex, career) => {
    switch (career) {
      case "headmaster":
        return new Headmaster(name, age, sex);
      case "teacher":
        return new Teacher(name, age, sex);
      case "student":
        return new Student(name, age, sex);
      case "cleaner":
        return new Cleaner(name, age, sex);
      default:
        return null;
    }
  };
}

// 检验任务是否能正确分配
const zhangsan = AssigningTask.JobAssigning("张三", 36, "man", "headmaster");
console.log("张三的任务是:", zhangsan.job()); // 张三的任务是: 管理学校

注意:在同一个文件中,需要把之前命名的张三变量注释掉,否则会报错。

在第一步中,我们通过静态方法 JobAssigning 对职业(career)的区分,进而返回不同构造器的实例。在这种情况下,就只需要对一个 AssigningTask 进行操作。

但是在这一步中,我们并没有实现对共性的抽离,且仍然需要书写大量的构造器,无需着急,在下一步时会同时解决这两个问题。

这里需要新建一个文件 index2.js ,并引入 index.html 中:

图片描述

第二步:抽离共性

// index2.js

// 抽离共性
class People {
  constructor(name, age, sex, career, job) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.career = career;
    this.job = job;
  }

  jobFn = () => {
    return this.job;
  };
}

// 区分非共性
class AssigningTask {
  constructor() {}

  // 静态方法:根据职务分配工作
  static JobAssigning = (name, age, sex, career) => {
    let job = "";
    switch (career) {
      case "headmaster":
        job = "管理学校";
        break;
      case "teacher":
        job = "传授知识";
        break;
      case "student":
        job = "学习知识";
        break;
      case "cleaner":
        job = "打扫卫生";
        break;
      default:
        job = "";
    }

    return new People(name, age, sex, career, job);
  };
}

希望各位同学能自己在实验环境中测试一下该方法的结果。注意的是,这里 People 类中的工作方法是 jobFn ,注意在调用时不要搞错了。

在上面的 People 类中,我们抽离出来了名字,年龄这些共有属性,随后在 AssigningTask 中不再返回各种不一样的构造器,而是仅仅用职务字段来区分不同的工作内容,然后作为参数传入新建的 People 类中即可。

通过这样的方式,我们就避免了数据大量且重复的构造器,不仅仅简化了工作的复杂度,在代码的阅读性和维护上都有了很大的改进。

到了这一步,已经基本实现了目标:分离“变的部分”与“不变的部分”,通过传参的方式来在“工厂”内部处理,我们无需但是过程如何,而是只看结果就行。

抽象工厂模式

有些情况下,我们的确可以使用简单工厂模式来重构,但不一定是最好的方案,或者说还能有更好的实现。

本小节要介绍的抽象工厂模式,它不像简单工厂那样好理解,却拥有更高的抽取能力,复用能力及维护能力。

仍然用上面的学校的例子,现在我们已经用简单工厂进行了一次重构,实现了共性的抽离,专注于非共性的改变。

但是这儿仍然存在一些问题,例如:

  1. AssigningTask 类中,静态方法 JobAssigning 里面存在大量的判断分支,意味着如果我们有一百个岗位,那么就有可能要写一百个判断条件去区分不同的职务。这会让我们对 JobAssigning 方法做很大的调整,且随时有可能调整。这一点就违背了“开放封闭”这一涉及原则。
  2. 目前只有数种共性,例如名字,年龄等,然而实际的场景下,人还具有更多的共性,例如身高,体重等等,那么如果要添加这些共性,那么过程一定是让人痛苦的,因为我们首先要在 People 类中添加,其次还要修改 AssigningTask 类中的 JobAssigning 方法。这同样会违背“开放封闭”原则。

我们先举一个简单的例子:

新建一个 abstract.js 文件,同样引入 index.html 中。

构建第一个抽象类(人),它拥有着最高的共性,即对任何人而言都是存在的。

// abstract.js

/**
 * 人的抽象类,包含了名称,年龄及吃这三个共性
 */
class PeopleAbstract {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log("人可以吃东西!");
  }
  work() {
    console.log("人可以工作!");
  }
}

人主要会分为男女,因此我们可以在构建 ManWoman 这两个类,使用 extends 关键词实现对 PeopleAbstract 类的继承,就像这样:

// abstract.js

// 男人
class Man extends PeopleAbstract {
  constructor(name, age, sex, beard) {
    super(name, age, sex);
    this.beard = beard;
  }
  work() {
    console.log("男人的工作");
  }
}

// 女人
class Woman extends PeopleAbstract {
  constructor(name, age, sex, hair) {
    super(name, age, sex);
    this.hair = hair;
  }
  work() {
    console.log("女人的工作");
  }
}

让我们看一下实际的效果:

// abstract.js

const man = new Man();
man.eat(); // 人可以吃东西!
man.work(); // 男人的工作

const woman = new Woman();
woman.eat(); // 人可以吃东西!
woman.work(); // 女人的工作

可以看到,ManWoman 实例化的对象不仅可以调用到父类的 eat 方法,并且还成功调用了自身重写的 work 方法。

小结一下:在 ManWoman 这两个子类中,不仅各自添加了属性,还重写了 work 方法,有没有感觉到一点方便的就是,我们对其中任意一个子类修改,都不会影响到另一个子类。

我们回到学校的例子,为了统一,这里把性别这一属性仍然与名字等放在一起,现在来重新构建 People 类:

继续在 abstract.js 中实现:

// abstract.js

class People {
  constructor(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }

  jobFn = () => {};
}

可以看到,我们不再传入 jobcareer 这两个参数,同时在 jobFn 方法中也不再有任何具体的操作。这样就实现了一个“抽象”的类,只定义了参数和方法,而不参与具体的实现。

接下来,利用 extends 关键字就可以单独的实现校长类:

// abstract.js

class Headmaster extends People {
  constructor(name, age, sex) {
    super(name, age, sex);
  }

  jobFn = () => {
    return `${this.name}的任务是:管理学校`;
  };
}

来看看效果如何:

// abstract.js

const zhangsan = new Headmaster("张三", 36, "man", "headmaster");
console.log(zhangsan.jobFn()); // 张三的任务是:管理学校

从打印结果上可以看到,Headmaster 完全满足要求,这个时候我们就可以依次把其他几种职务依次实现一下:

// abstract.js

class Teacher extends People {
  constructor(name, age, sex) {
    super(name, age, sex);
  }

  jobFn = () => {
    return `${this.name}的任务是:传授知识`;
  };
}

class Student extends People {
  constructor(name, age, sex) {
    super(name, age, sex);
  }

  jobFn = () => {
    return `${this.name}的任务是:学习知识`;
  };
}

class Cleaner extends People {
  constructor(name, age, sex) {
    super(name, age, sex);
  }

  jobFn = () => {
    return `${this.name}的任务是:打扫卫生`;
  };
}

至此,整个例子就结束了,不知道各位同学到这里是否有新的感悟。

在抽象设计模式中,我们真正的将共性单独的抽离出去,并且仅仅做了属性与方法的定义而不参与具体实现,通过类继承的方式去分别实现了不同的职务类。并且当你对各个子类添加或修改时,是不会影响到父类及其他的子类的,这让后续的代码维护变得方便很多。

也许会有同学问,这样子和我们一开始分开定义各个职务不是一样的嘛。

当然不是,这里做一个简单的区分:

在开始的实现中,我们在每一个类中都写了名字,年龄这些属性,试想如果存在 100 个属性那就意味着需要在每个类中都要写 100 个属性,这个工作不仅繁复且后期不易维护。

而在抽象工厂的思想下,我们是通过 extends 这样继承的关系,仅仅需要通过 super 方法就能调用到所有父类的属性,因此只需要在父类中书写属性,而在子类中直接继承即可。这一点你完全可以从代码量上看出来。

让我们来总结一下抽象工厂:它可以把共有的属性抽出去形成抽象类,这个抽象类(People)不负责实例化,它仅仅用来对一些公有的属性或方法进行定义。具体的实例化由继承该抽象类的具体类去完成的。

工厂模式的应用

工厂模式的应用非常广泛,回到我们在基本概念中举到的张三买车的例子。

新建一个 car.js 文件,并引入 index.html 中。

首先就需要一个 car 的抽象类来定义车的共性:

// car.js

// 车的抽象类
class Car {
  constructor(brand, color, price) {
    this.brand = brand;
    this.color = color;
    this.price = price;
  }

  // 综合结论
  conclusion = () => {};
}

为了方便举例,这里就取奔驰与比亚迪这两种为例:

// car.js

// 奔驰
class Benz extends Car {
  constructor(brand, color, price, type) {
    super(brand, color, price);
    this.type = type;
  }

  conclusion = () => {
    return `品牌:${this.brand},颜色:${this.color},价格:${this.price},它是一辆${this.type}`;
  };
}

// 比亚迪
class Byd extends Car {
  constructor(brand, color, price, type) {
    super(brand, color, price);
    this.type = type;
  }

  conclusion = () => {
    return `品牌:${this.brand},颜色:${this.color},价格:${this.price},它是一辆${this.type}`;
  };
}

此时根据张三的需求,我们可以给出两种车型:

// car.js

// 1、张三希望要一辆红色,价格30万,进口的奔驰车
const benz = new Benz("奔驰", "红色", "300000", "进口车");
console.log(benz.conclusion()); // 品牌:奔驰,颜色:红色,价格:300000,它是一辆进口车

// 2、张三希望要一辆黑色,价格35万,国产的比亚迪车
const byd = new Byd("比亚迪", "黑色", "350000", "国产车");
console.log(byd.conclusion()); // 品牌:比亚迪,颜色:黑色,价格:350000,它是一辆国产车

这类似的例子在生活中举不胜举,只要大家在理解工厂模式的基础上去细心发现,一定能发现很多工厂模式的应用。

实验挑战

在现代社会中,手机已经非常普遍,因此希望各位同学能借鉴前面的例子,利用工厂模式的思维对手机进行品牌的抽象实现。

要求:

  1. 分为苹果和安卓两个品牌,它们的共性包括品牌(brand)和尺寸(size);
  2. 苹果新增创始人(originator)属性,安卓新增发布时间(time)属性;
  3. 为它们添加 show 方法,通过打印方式输出描述内容。

希望能利用这个课后小例子加深大家对工厂模式的认识。

答案代码放在课程的源码包中的 phone.js 文件,大家可以自行下载,和自己的实现方式进行比对。( 源码下载地址

小结及下节实验内容预告

至此,关于工厂模式的介绍基本告一段落了,这里可以为大家学习提供两点建议:

  1. 了解一下 JAVA 中的抽象类,可以帮助我们理解这里的抽象工厂;( 可参考: JAVA 抽象类
  2. 从例子出发,自己从举例中剥离出抽象类的定义;

下一节实验会为大家带来的是单例模式,有时间的同学可以先自行的去简单了解一下什么是单例模式,而单例模式在前端中的应用有哪些。相信通过提前的预习和对问题的思考,下一节实验一定会学习的更加轻松,也更容易理解。

标签:name,创建,age,js,sex,job,工厂,设计模式,class
From: https://www.cnblogs.com/xzemt/p/18030129

相关文章

  • 设计模式创建型之单例模式
    实验介绍本实验主要介绍了设计模式中的单例模式,在前端领域中,有很多地方都运用到了单例模式的思维,例如目前的主流前端框架中所用到的Redux和Vuex。实验首先通过一个小例子为大家展示了单例模式的实现原理,随后通过完成一个自定义的Storage存储器来帮助大家加深对单例模式的理......
  • 【Python&GIS】Python线矢量等距离取点/线等分取点&点创建矢量面
    ​        不多说,这是之前项目需求的代码,已经是去年的了一直没来的及发,今天抽出来一丢丢的空挡发一下。主要就是利用线矢量等距离生成点矢量,或者直接将线矢量等分生成点矢量,这个需求其实极限一下就是线转点了(将距离设置小一点)。顺便将点生成矩形面的代码也给出来,这里的......
  • 3分钟看懂设计模式02:观察者模式
    一、什么是观察者模式观察者模式又叫做发布-订阅模式或者源-监视器模式。结合它的各种别名大概就可以明白这种模式是做什么的。其实就是观察与被观察,一个对象(被观察者)的状态改变会被通知到观察者,并根据通知产生各自的不同的行为。以下为《设计模式的艺术》中给出的定义:观察者......
  • CreateHolesInImage说明文档-对于遥感影像的空洞创建多边形矢量数据
    提取遥感影像的空洞地理处理工具箱特点:通用地理处理工具,支持任何遥感影像,包括无人机,卫星遥感,普通图片和gdb,mdb数据库等。速度快,极致效率,效率高,支持对多个文件夹下的任意多数据进行批处理使用简单,全自动话,无人工干预功能:提取空洞提取空洞和非空洞默认临时文件夹,结果文件夹默认临时......
  • 在K8S中,请问harbor的secret创建能否直接创建资源清单?
    答案:当然可以,在Kubernetes(简称K8S)中,为了允许集群中的Pod能够从Harbor私有仓库拉取镜像,您可以直接通过编写资源清单(YAML文件)来创建一个Secret对象。这个Secret将包含访问Harbor所需的认证信息。以下是一个示例:apiVersion:v1kind:Secretmetadata:name:harbor-registry-secr......
  • 02. Create Project 创建项目导入素材
    使用版本我因为需要开发微信小游戏,所以使用的是2022.3.8f1c1版本创建项目首先创建一个3D项目,因为后续我们需要学习如何将一个普通的项目升级为URP通用渲染管线安装URP通用渲染管线在PackageManager中搜索Universal,然后安装UniversalRP。2022版本的Unity不需......
  • [设计模式]创建型模式-抽象工厂模式
    简介抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式将一组具有共同主题的单个工厂封装起来,它提供接口用于创建相关或依赖对象的家族,而不需要指定具体的类。抽象工厂模式包含以下几个核心角色:抽象工厂(A......
  • 5 - 设备文件创建
    DeviceFileCreation原文链接我的博客创建字符设备设备文件在上一个小节中,我们知道了如何分配主副设备号,但是到此为止,只是创建主副设备号。并未在/dev目录下创建设备文件。设备文件设备文件可以实现用户空间应用与硬件的通讯。它们并不是普通文件,只是从编程视角来看是一......
  • AI智能分析网关V4智慧工厂视频智能监管与风险预警平台建设方案
    一、背景需求分析1)随着信息技术的迅猛发展和制造业竞争的加剧,智慧工厂成为了推动制造业转型升级的重要引擎。智慧工厂解决方案通过整合物联网、人工智能、大数据分析等先进技术,实现生产过程的智能化、自动化和高效化,为企业提供了更加灵活、智能的生产模式和管理方式。2)工厂生产......
  • RAID类型介绍、创建、彻底删除
    目录一、RAID(磁盘阵列)    1.1、概念    1.2、RAID0(条带化存储)    1.3、RAID1(镜像存储)    1.4、RAID5     1.5、RAID6       1.6、RAID1+0(先做镜像,再做条带)    1.7、RAID0+1(先做条带,再做镜像......