工厂设计模式看这一篇就够了

news2024/11/24 14:43:19

本文将重点介绍几种工厂设计模式:简单工厂、工厂方法模式、抽象工厂模式和建造者模式。这几种设计模式在生产制造的流程下层层递进,可以满足不同的使用场景。在实际运用时,没有一个万能的工厂模式可以套用,要结合具体业务场景选择合适的设计模式。大部分童鞋很容易将这几种工厂模式弄混淆,所以建议大家收藏+关注,以后要用到了方便查阅。

为什么需要工厂模式?

在面向对象的世界里,世间万物皆是制造(实例化和初始化)出来的。如果只是单单构造出一两个对象,代码相对来说并不复杂,可是一旦需要构造成百上千的对象,每种对象的构造过程又不尽相同,这个时候就需要利用工厂方法进行封装,达到降低系统耦合和代码复用的目的。

在现实生活中,我们可以找到很多这样的例子。例如,想要在飞行游戏里生产一辆坦克和一架飞机,假设即使靠我自己的能力也能生产出来,那也是想当复杂的一件事情。不仅如此,如果我需要在不同的时间,不同的地点生产100辆坦克和50架飞机,我是不是就需要将生产的过程重复150次?

有经验的程序员立刻就能想到将坦克和飞机的制造过程独立成一个方法,每次生产坦克或飞机的时候调用这个方法即可,这样就达到了代码复用的目的。没错,这正是简单工厂的雏形,只不过这个生产方法是交由一个工厂类来管理。

简单工厂

我们以飞行游戏生产敌人为示例,介绍一下简单工厂的实现原理。首先我们对游戏敌军建模。坦克和飞机作为敌军肯定存在一定的共性,它们都需要一个坐标属性用来描述位置,它们还需要一个在地图上绘制的行为属性。所以我们可以给它们设计一个敌人的抽象类,如图所示。

坦克类继承敌人这个抽象父类,实现代码如图所示。

飞机类继承敌人这个抽象父类,实现代码如图所示。

通过上述代码可以看出,坦克和飞机都继承自敌人这个抽象父类,共用了父类的坐标属性,并且实现了各自的绘制方法show()。现实中的游戏角色肯定没有这么简单,这个示例只是为了让大家更好的理解简单工厂的原理。

游戏敌人的建模完成以后,就需要对游戏敌人进行实例化和初始化了。显然这个过程需要两步:

1、构造敌人时初始化坐标位置;

2、根据坐标在地图上绘制敌人。

我们在客户端里实现这部分逻辑,如图所示。

客户端的代码非常简单,我们生产了一架飞机和一辆坦克,它们的横坐标是随机的,纵坐标是0,表示敌人登场亮相在地图的最上面。

看出来有什么问题了吗?对,前面我们已经提到了,生产一个敌人无所谓,但是如果要生产很多的敌人,为了代码的复用,我们可以将生产过程放到一个方法里,这个方法用一个工厂类来管理,通过传递敌人的类型返回敌人对象,这就是简单工厂。

有了简单工厂类,我们在客户端里的实现逻辑就可以省去不少重复代码。

这样看来代码确实更加简洁明了。以后我们想要在任何地方生产敌人,都可以通过直接调用简单工厂类的create()方法来实现。

小结

使用目的,是为了将生产流程抽离出来,达到复用和解耦的目的。

实现方式,设计一个工厂父类,实现一个生产方法,该方法通过判断工厂类型来决定执行哪个生产流程。

使用场景,是生产需求简单不多变时。同时,只生产一种产品时也没有必要使用简单工厂。

缺陷,虽然简单工厂可以共用产品的生产过程,但其扩展性不好。因为简单工厂的生产方法和产品类型产生了强耦合,所有生产方式堆积在一个简单工厂类中。

工厂方法模式

前面已经提到了简单工厂的缺点是扩展性不好。由于工厂类的生产方法与产品类型强耦合,所以每增加一个新的产品类型,就需要修改工厂类的生产方法。

那么,有没有什么好办法可以解决这个问题呢?当然有了,我们可以拆分生产方法,将产品模式抽象化、多态化,这就可以实现生产方法的多态性。怎么,看不懂我说的吗?下面我要说人话了。工厂方法模式的核心是定义一个工厂接口,以达到确定工业制造标准的目的。如果还是没明白什么意思,那我就举一个简单的例子你就会明白了。

小米、华为和联想都有自己的鼠标生产工厂,尽管它们生产出来的鼠标从外观上来看大小各异,色彩缤纷,但是它们生产出来的有线鼠标都需要遵守USB接口的工业制造标准,否则用户就用不了它们的鼠标。这里的标准映射到面向对象的设计模式当中,指的就是工厂接口。只要各个制造工厂都实现工厂接口,以后增加新的产品就不会影响到以前的产品及其工厂。

我们这次使用工厂方法模式实现上面的飞行游戏生产敌人的需求。假设现在我们除了要生产坦克和飞机以外,还要在每一关的最后增加一个大Boss,如果使用简单工厂来实现,需要修改简单工厂类的create()方法。根据开闭原则(对扩展开放,对修改关闭),我们应该避免修改以前的代码逻辑,所以我们这次使用工厂方法模式来实现。

首先制定工业制造标准:一个工厂接口。

基于工厂接口,实现坦克工厂。

基于工厂接口,实现飞机工厂。

基于敌人抽象类,实现大Boss。

基于工厂接口,实现大Boss工厂。可以发现在生产大Boss时和生产坦克、飞机的不同,大Boss的亮相是在屏幕顶部的中间。

下面是客户端实现代码,分别生产了5辆坦克,5架飞机和1个大Boss。

使用工厂方法模式之后的客户端,在生产各种不同的敌人时都采用了统一的生产标准,这样做的好处就是不但代码更加精炼,而且以后再生产新的类型的敌人也不会对已经实现的原有代码做任何调整。

小结

使用目的,为了增加产品的扩展性和灵活性。

实现方式,设计一个工厂制造接口作为生产方法的标准,满足了工厂的多态性。

使用场景,适用于需要不断增加新的产品的场景,并且每种产品的制造标准统一,没有差别。

缺陷,每增加一个产品就需要增加一个对应的工厂,容易造成工厂泛滥。

工厂方法模式可被视为对简单工厂模式的进一步升华与扩展,而工厂方法模式才真正称得上是设计模式。在工厂方法模式的结构中,不仅对产品类别进行了区分,同时对工厂也进行了分类处理。相较于将所有制造逻辑集中在一个单一的简单工厂类内部,更倾向于将这些生产方式分配到各个特定的子类工厂中去具体实现。这种设计方式极大地增强了工厂的抽象性和多态性,有效避免了因新增产品类导致频繁修改同一工厂类的问题,使得后期代码维护和扩展工作变得更加直观、便捷。

抽象工厂模式

如果说工厂方法是对制造方法的抽象化,那么抽象工厂就是对工厂本身的抽象化。为了调和工厂泛滥的矛盾,抽象工厂像工厂方法模式一样,通过接口实现产品的扩展性,但它们之间最大的不同在于抽象工厂不局限于生产一种产品,用人话来解释就是一个抽象工厂有多个工厂方法,每个工厂方法生产一种产品。所以,抽象工厂模式可以看作是工厂方法模式的加强版。

举个栗子,华为手机有P系列和Mate系列,苹果手机有iphone系列与iphone plus系列。如果采用工厂方法模式来生产华为和苹果手机,那么就需要P系列、Mate系列、iphone系列和iphone plus系列四个工厂来分别生产。但是如果改用抽象工厂模式,我们可以设计一个抽象工厂接口,该接口有两个工厂方法:小尺寸手机生产方法和大尺寸手机生产方法。

首先创建一个手机虚类。一个手机有长、宽、高和颜色属性,还有一个打电话的行为属性。

然后创建小尺寸手机虚类和大尺寸手机虚类。小尺寸手机长100,宽50,高10,大尺寸手机长150,宽70,高10,大尺寸手机屏幕更大。

分别继承小尺寸手机和大尺寸手机,得到华为和苹果的各两款系列机型。

有了手机产品之后,就要给对应的产品添加工厂。虽然这里我们生产了华为和苹果的各2款共4款手机,但是我们采用抽象工厂模式,只需要创建两个工厂就行。

就此我们已经完成了抽象工厂模式的架构,下面通过一个客户端对象学习一下如何使用。

工厂的工厂

理解了抽象工厂模式的原理,我们还可以更加深入一点理解一下什么是“工厂的工厂”。前面介绍的设计模式都在做一件事情,就是用工厂来生产产品。那么你是否有想过,工厂由谁来生产?也许你会说工厂在代码里面直接new就可以了嘛。对,这样当然没有错,但是有的时候工厂的创建也是需要多态性和扩展性的。

例如上面生产手机的例子。假设手机市场正在处于2023年和2024年过度阶段,要同时生产2023的老款和2024年的新款手机,也就是说我们除了要通过华为手机工厂和苹果手机工厂来区分手机品牌,还要通过手机工厂的工厂方法来区分手机尺寸(手机系列),甚至于我们还要告诉工厂是生产去年的老款还是今年的新款。所以,我们就需要再设计一个工厂,工厂的方法生产的是2023工厂或者2024工厂,这时就可以把品牌工厂(华为/苹果工厂)看作是产品。

工厂的工厂非常适合在代码里消除层层嵌套的if/else,往往在代码最开始的地方将代表产品性质的参数传入工厂构造函数,就能直接通过多态性得到我们想要的产品,而不会在代码里看到if/else判断。

小结

使用目的,防止工厂泛滥。

实现方式,一个工厂虚类,多个生产方法。工厂虚类的工厂实现类划分产品族,生产方法划分产品系列。

使用场景,适合于大规模的产品生产,特别是可以按族和系列来划分的产品。

缺陷,增加了代码的复杂度。

抽象工厂模式要求产品系列相对固定,而产品族可以灵活多变。

建造者模式

前面介绍的工厂设计模式系列,它们有一个共同点就是只能为一个产品提供一种制造方法。这和现实生活中的工厂生产产品如出一辙,李宁和安踏的工厂只能生产李宁和安踏的鞋子。

现在我们考虑这样一种场景,华为想要生产手机上使用的麒麟芯片,海思负责的芯片设计已经完成,需要一个芯片代工厂量产芯片。它们找到了台积电代工,刚开始一切顺利,芯片交付了几百万件,性能远超同类手机。美国眼看自家的苹果iphone手机干不过华为,想出阴招对台积电出口管制,不允许为华为代工芯片。无奈之下,华为只好加速国产化进程,在某些神秘国产代工厂的帮助下,华为Mate60手机实现了国产芯片全替代的壮举。为什么华为芯片的生产可以更换代工厂?

理解了前面介绍的工厂模式系列的原理,我们不难发现如果华为芯片制造使用的是这些工厂模式,那么华为寻找新的芯片代工工厂成本肯定会非常之大。那么华为是怎么做的呢?答案就是华为使用了建造者模式。建造者模式的核心就是将“制造工序标准化”,从而达到“制造工艺多样化”的目的。在上述华为芯片的案例中,华为将制造工序的标准交给海思把控,这样不论是哪个代工厂以什么样的制造工艺来生产芯片都可以,前提是只要达到了海思制定的制造工序标准。

下面我们通过代码来感受一下建造者模式的魔力。这是一个关于房屋建造的案例,一家房地产公司需要建造房屋。为了简化房屋的建造,这里的房屋只由地基、墙体和房顶组成。

房屋的建造需要找一个施工方,为了保证质量,房地产公司会招投标寻找合适的施工方,同时房地产公司也提出了建造房屋的标准。标准非常简单,只需要建造地基、墙体和房顶即可,最后有一个验收的行为。

这时有两个施工方来招投标,一个是别野施工方。

还有一个是公寓施工方。

房地产商虽然制定了标准,但还是不放心施工方的质量,所以房地产商又找来了一个工程总监来把控施工流程的质量。工程总监并不关心由谁来完成房屋的建造,也不会关心施工方的制造工艺,他只需要保证施工方的建造工序。这就是前面说的“制造工序标准化,制造工艺多样化”,即制造工序和制造工艺解耦。

最后,在进行招投标的时候进行成果展示。

查看执行结果。

小结

使用目的,在相同的建造工序场景下,生产出不同的产品。

实现方式,制定建造标准(Builder接口)。由指导者(Director)指导建造者实现类(ConcreteBuilder)执行标准,生产出产品(Product)。

使用场景,适用于产品复杂的场景,制造工序稳定而制造工艺多样。和工厂模式不同的是,工厂模式只需要生产出产品而不关心产品的组成,而建造者模式除了要生产出产品,还要关心产品的组成和生产过程。前者注重获得产品,后者注重如何建造并获得产品。

缺陷,生产产品的过程更加复杂,多了建造者和指导者的类。

严格来说,建造者模式不隶属于工厂设计模式家族,但在讨论中我们将两者共同探讨,主要原因在于它们均属于创建型设计模式这一大类。建造者模式,又名生成器模式,其核心作用在于处理复杂对象的构造与初始化过程,通过有序地逐个整合多个简单组件对象,最终形成一个结构复杂的完整对象实例。相较于工厂模式系列,建造者模式的独特之处在于它着重于将复杂的构建步骤从产品类和工厂类的具体实现中抽象出来,并使其独立运作。这样,一套标准化的构建流程可以灵活适应不同的产品配置,从而产生形态各异的产品实例。

总结

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

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

相关文章

【华为GAUSS数据库】IDEA连接GAUSS数据库方法

背景:数据库为华为gauss for opengauss 集中式数据库 IDEA提供了丰富的各类型数据库驱动,但暂未提供Gauss数据库。可以通过以下方法进行连接。 连接后, 可以自动检查xml文件中的sql语句是否准确,表名和字段名是否正确还可以直接在…

基于 IoT 物联网 + 5G 技术搭建 100万台电梯智能化运维平台

随着近20年我国房地产的蓬勃发展,电梯已经成为人们现代生活中不可或缺的一部分,也是城市化建设中重要的建筑设备之一。据中国电梯行业协会统计,截至2022年底,我国电梯保有量为990万台,电梯运营健康度,减少事…

Pyro —— Sparse vs dense simulations

目录 Simulation area Sparse solving Understanding resizing Simulation area 在模拟的期间,pyro场都在当前容器内定义;开始非常小,随模拟的进行,解算器会不断的对其扩展或收缩;为重置流体框,解算器会…

Android 基础技术——addView 流程

笔者希望做一个系列,整理 Android 基础技术,本章是关于 addView 在了解 addView 流程之前,先回答下以下几个问题: PhoneWindow是什么时候创建的? DectorView 是什么? DectorView 是什么时候创建的&#xf…

conda国内加速

1、配置国内源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ 2、显示源地址 conda config --set show_channel_urls yes

外呼机器人有什么优势?

外呼机器人有什么优势?值得受到大多数电销企业的追捧! 1、电话外呼效率高: 每天可拨打的电话数量是人工的5-10倍,人工一天只能拨打200-300通电话,机器人每天能打3000通电话以上,无须休息,按照…

PWM之舵机

舵机又称直流电机,如下图 本节承接上节,具体的PWM技术已经在上一节讲的很详细了,本节就不再讲了,那么我们的重点就放在直流电机的工作原理上了。 一、工作原理 我们研究直流电机,主要式研究直流电机旋转速度的调节&a…

linux LPT和COM回路测试(基于python+Qt+C++)

软件UI: 回路治具&#xff08;COMLPT&#xff09;&#xff1a; lpt_test.cpp&#xff08;c 源代码&#xff09;&#xff1a; #include <iostream> #include <fstream> #include <sstream> #include <unistd.h> #include <fcntl.h> #include <…

什么是游戏盾?哪家效果好。

游戏盾是什么呢&#xff0c;很多做游戏开发的客户估计都是听说过的&#xff0c;但是也不是所有的游戏开发者会运用到。因为&#xff0c;游戏盾是针对游戏行业APP业务所推出的高度可定制的网络安全管理解决方案&#xff0c;除了能针对大型DDoS攻击(T级别)进行有效防御外&#xf…

Python(18)--文件输入/输出 Ⅱ

​ 大家好&#xff01;我是码银&#x1f970; 欢迎关注&#x1f970;&#xff1a; CSDN&#xff1a;码银 公众号&#xff1a;码银学编程 前言 前一篇文章&#xff08;python(17)–文件的输入/输出-CSDN博客&#xff09;介绍了如何操作文本文件和二进制文件&#xff0c;以及对应…

设计亚马逊按销售排名功能

1&#xff1a; 定义 Use Cases 和 约束 Use cases 作用域内的Use Case Service 通过目录计算过去一周内最受欢迎的产品User 通过目录去View过去周内最受欢迎的产品Service 有高可用 作用域外 整个电商网站 设计组件&#xff08;只是计算销售排名&#xff09; 约束和假设…

【Python】模块

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

C++ :命名空间域

目录 冲突与命名&#xff1a; 举个例子&#xff1a; 全局与局部&#xff1a; 域作用限定符&#xff1a; 命名空间域&#xff1a; 冲突与命名&#xff1a; 在C语言中&#xff0c;我们通常会使用stdlib.h 而stdlib.h 本质上是一个函数的库&#xff0c;在程序中使用的大多数…

django邮件通知功能-

需求&#xff1a; 1&#xff1a;下单人员下订单时需要向组长和投流手发送邮件通知 2&#xff1a;为何使用邮件通知功能&#xff1f;因为没钱去开通短信通知功能 设计 1&#xff1a;给用户信息表添加2个字段 第一个字段为&#xff1a;是否开通邮件通知的布尔值 第二个字段为: 用…

Python 折线图的绘制(Seaborn篇-04)

Python 折线图的绘制(Seaborn篇-04)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

详解Redisson

第1章&#xff1a;Redisson简介 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊Redisson&#xff0c;Redisson不只是简单地对Redis进行了封装&#xff0c;它还提供了一系列高级的分布式Java数据结构&#xff0c;像是分布式锁、原子长整型这种。 首先&#xff0c;Redi…

React 初次接触

背景 还是为了完善高大上的在线文档系统&#xff0c;虽然比着葫芦画瓢的修改了一些所谓的代码&#xff0c;慢慢的才发现&#xff0c;原来这就是传说中的React&#xff0c;所以有比较又要囫囵吞枣一下React。 基本原理 参照《React技术揭秘》 网上有电子版 &#xff0c;应该是…

Makefile编译原理 条件判断语句

一.makefile中支持条件判断语句 可以根据条件的值来决定make的执行 可以比较两个不同变量或者变量和常量值 条件判断语句语法说明&#xff1a; 条件判断关键字&#xff1a; 条件判断语句注意点&#xff1a; 条件判断语句之前可以有空格&#xff0c;但不能没有Tab字符&#x…

【Java】SpringBoot实现事件监听(异步执行)

在Spring Boot中&#xff0c;事件监听是一种机制&#xff0c;通过该机制&#xff0c;你可以定义和触发自定义的事件&#xff0c;以及在应用程序中注册监听器来响应这些事件&#xff0c;提供了一种解耦的方式来处理应用程序中的事件。 文末有源码gitee地址&#xff01;拉取可进…

MySQL基础笔记(8)多表查询

一.多表关系介绍 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也会存在着各种联系&#xff0c;分为如下3类&#xff1a; 一对…