23种设计模式之工厂模式

文章目录

工厂模式

工厂模式是一种创建对象设计模式,将对象的创建和使用分离,可提高代码可维护性、可扩展性,封装复杂创建逻辑。其目的是隐藏对象创建的过程与细节。

在这里插入图片描述

工厂模式经典案例

国际化案例,根据用户的IP返回不同语言的界面。

在这里插入图片描述

工厂模式分类与优缺点

工厂模式主要分为三类:

一、简单工厂模式

简单工厂模式又称为静态工厂方法模式。它定义了一个创建对象的类,由这个类来封装实例化对象的行为。在简单工厂模式中,工厂类有一个创建产品对象的方法,这个方法可以根据传入的参数决定创建哪种具体的产品对象。

优点:实现了对象创建和使用的分离,客户端无需了解对象的具体创建过程,只需要关心如何使用对象。代码结构简单,容易理解和实现。

缺点:不符合开闭原则,即当需要增加新的产品对象时,需要修改工厂类的代码。

二、工厂方法模式

工厂方法模式是在简单工厂模式的基础上,将工厂类的创建方法抽象成抽象方法,由具体的工厂子类实现这些抽象方法来创建具体的产品对象。

优点:符合开闭原则,当需要增加新的产品对象时,只需要增加一个具体的工厂子类,无需修改原有代码。

缺点:工厂子类过多时,会导致代码结构复杂,增加系统的维护成本

三、抽象工厂模式

抽象工厂模式中,工厂类不再负责创建具体的产品对象,而是负责创建一系列相关的产品对象。客户端只需要调用抽象工厂的抽象创建方法,由具体的工厂子类来创建所需的产品对象系列。

优点:代码更加灵活和可维护,当需要增加新的产品对象系列时,只需要增加一个具体的工厂子类,无需修改原有代码。

缺点:实现复杂,不适合简单的应用场景。对开闭原则的支持也有一定的局限性,当需要增加新的产品对象种类时,需要修改抽象工厂和所有具体工厂的代码。

工厂模式的使用场景

基础使用:

  • 当对象创建涉及多步骤及依赖关系,工厂模式可封装复杂创建逻辑,让调用方代码更简洁。
  • 需根据不同条件创建不同类型对象,工厂模式能动态确定创建逻辑。
  • 期望统一管理对象创建以利维护和扩展,工厂模式可集中处理创建过程。

高级使用:

  • 与其他模式混用,如工厂模式与策略模式结合,或工厂模式、策略模式、模板模式共同使用。

工厂模式的优点

一、解耦

  • 工厂模式将对象的创建和使用分离,使得代码的各个部分之间的耦合度降低。使用者无需了解对象的具体创建过程,只需要从工厂获取所需的对象即可。这样,当对象的创建逻辑发生变化时,不会影响到使用对象的代码部分。
  • 例如,在一个电商系统中,订单模块需要使用支付对象进行支付操作。如果不使用工厂模式,订单模块可能直接实例化支付对象,这样订单模块就与具体的支付对象创建过程紧密耦合。当支付方式发生变化时,订单模块的代码需要进行相应的修改。而使用工厂模式,订单模块只需要从支付工厂获取支付对象,无需关心支付对象的具体创建过程,从而实现了解耦。

二、代码复用

  • 工厂类可以被多个地方复用,提高了代码的复用性。工厂类通常封装了对象的创建逻辑,可以根据不同的条件创建不同类型的对象。这些创建逻辑可以在多个地方重复使用,避免了代码的重复编写。
  • 例如,在一个图形绘制系统中,有多种不同类型的图形(圆形、矩形、三角形等)需要创建。可以创建一个图形工厂类,该工厂类可以根据不同的参数创建不同类型的图形对象。这样,在多个绘图模块中都可以复用这个图形工厂类,提高了代码的复用性。

三、符合迪米特法则和单一职责原则

  • 迪米特法则也称为最少知识原则,它强调一个对象应该对其他对象有尽可能少的了解。工厂模式将对象的创建逻辑封装在工厂类中,使用者只需要与工厂类进行交互,无需了解对象的具体创建过程,符合迪米特法则。
  • 单一职责原则要求一个类应该只有一个引起它变化的原因。工厂类的职责就是创建对象,它将对象的创建逻辑集中在一个地方,使得代码的职责更加清晰,符合单一职责原则。
  • 例如,在一个企业管理系统中,员工对象的创建可能涉及到从数据库中读取员工信息、验证员工数据的合法性等复杂的操作。如果将这些创建逻辑分散在各个使用员工对象的模块中,会导致代码的职责不清晰,违反单一职责原则。而使用工厂模式,将员工对象的创建逻辑封装在员工工厂类中,使得代码的职责更加清晰,符合单一职责原则。

简单工厂模式实现

以下是一个用于创建不同类型的手机。

一、定义手机产品接口

java">/*** 手机接口*/
public interface Phone {void call();void sendMessage();
}

二、创建具体手机产品类

java">/*** 安卓手机*/
public class AndroidPhone implements Phone {@Overridepublic void call() {System.out.println("Android手机正在通话。");}@Overridepublic void sendMessage() {System.out.println("Android手机正在发送消息。");}
}/*** 苹果手机*/
public class IPhone implements Phone {@Overridepublic void call() {System.out.println("iPhone正在通话。");}@Overridepublic void sendMessage() {System.out.println("iPhone正在发送消息。");}
}

三、创建简单工厂类

java">public class SimplePhoneFactory {/*** 根据传入的手机类型决定创建哪种具体的手机对象*/public static Phone createPhone(String phoneType) {if ("android".equals(phoneType)) {return new AndroidPhone();} else if ("iphone".equals(phoneType)) {return new IPhone();} else {return null;}}
}

四、使用简单工厂-测试

java">public class SimpleFactoryTest {public static void main(String[] args) {Phone androidPhone = SimplePhoneFactory.createPhone("android");if (androidPhone != null) {androidPhone.call();androidPhone.sendMessage();}System.out.println("--------------------------------");Phone iPhone = SimplePhoneFactory.createPhone("iphone");if (iPhone != null) {iPhone.call();iPhone.sendMessage();}}
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
--------------------------------
iPhone正在通话。
iPhone正在发送消息。

总结

简单工厂模式成功地实现了手机的创建与使用之间的解耦。这使得使用者无需操心手机具体的创建流程,仅需从工厂获取所需的手机即可。然而,此模式存有一定弊端。后续若要增添新的手机类型,就必须持续地对工厂类进行修改,如此一来,维护成本便会相对较高。在简单工厂模式里,需依据方法的输入,编写大量的 if-else 语句来判断并创建不同的对象。

工厂方法模式实现

工厂方法模式可视为简单工厂模式的升级版。它针对简单工厂模式在扩展时需改动工厂代码的缺点,对工厂类进行了优化改进。通过这种方式,工厂方法模式有效地解决了简单工厂模式在扩展性方面的不足。

例子还是使用上面的Phone,实现方法如下。

一、定义手机产品接口

代码和简单工厂模式一样。

二、创建具体手机产品类

代码和简单工厂模式一样。

三、定义抽象工厂接口

java">public interface PhoneFactory {Phone createPhone();
}

四、创建具体工厂类

java">public class AndroidPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new AndroidPhone();}
}public class IPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new IPhone();}
}

五、使用工厂方法模式

java">public class FactoryMethodTest {public static void main(String[] args) {PhoneFactory androidPhoneFactory = new AndroidPhoneFactory();Phone androidPhone = androidPhoneFactory.createPhone();androidPhone.call();androidPhone.sendMessage();System.out.println("--------------------------------");PhoneFactory iPhoneFactory = new IPhoneFactory();Phone iphone = iPhoneFactory.createPhone();iphone.call();iphone.sendMessage();}
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
--------------------------------
iPhone正在通话。
iPhone正在发送消息。

总结

其结果与简单工厂模式相同。二者的主要区别在于:简单工厂模式仅有一个工厂类,负责创建所有产品对象;而工厂方法模式则演变为多个工厂,每个工厂对应一种特定的产品创建。这样一来,后续扩展时只需实现工厂接口即可创建新的产品对象。然而,该模式的缺点是随着产品种类的增加,工厂类的数量也会越来越多。

抽象工厂模式实现

抽象工厂模式可视为工厂方法模式的一种扩展。该模式主要用于解决一类产品的创建问题。在抽象工厂模式中,工厂类的职责并非创建具体的单一产品对象,而是负责创建一系列相关的产品对象。例如,在某些场景下,工厂不仅要创建手机,还需创建与之对应的手机配件。对比工厂方法模式,最直观的区别在于工厂方法模式中的PhoneFactory接口通常只有一个创建手机的方法,而抽象工厂模式AbstractPhoneFactory接口则拥有多个方法,分别用于创建不同的相关产品对象。

下面是简单画的图。

在这里插入图片描述

一、定义手机产品接口

上面有代码。

二、创建具体手机产品类

上面有代码。

三、定义手机配件接口

java">public class AndroidCharger implements PhoneAccessory {@Overridepublic void describe() {System.out.println("安卓充电器。");}
}public class IPhoneCharger implements PhoneAccessory {@Overridepublic void describe() {System.out.println("苹果充电器。");}
}

五、定义抽象工厂接口

java">public interface AbstractPhoneFactory {/*** 创建手机*/Phone createPhone();/*** 创建配件*/PhoneAccessory createAccessory();
}

六、创建具体工厂类

java">public class AndroidFactory implements AbstractPhoneFactory {@Overridepublic Phone createPhone() {return new AndroidPhone();}@Overridepublic PhoneAccessory createAccessory() {return new AndroidCharger();}
}public class IPhoneFactory implements AbstractPhoneFactory {@Overridepublic Phone createPhone() {return new IPhone();}@Overridepublic PhoneAccessory createAccessory() {return new IPhoneCharger();}
}

七、使用抽象工厂模式

java">public class AbstractFactoryTest {public static void main(String[] args) {AbstractPhoneFactory androidFactory = new AndroidFactory();Phone android = androidFactory.createPhone();PhoneAccessory androidAccessory = androidFactory.createAccessory();android.call();android.sendMessage();androidAccessory.describe();System.out.println("----------------------------------");AbstractPhoneFactory iPhoneFactory = new IPhoneFactory();Phone iphone = iPhoneFactory.createPhone();PhoneAccessory iphoneAccessory = iPhoneFactory.createAccessory();iphone.call();iphone.sendMessage();iphoneAccessory.describe();}
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
安卓充电器。
----------------------------------
iPhone正在通话。
iPhone正在发送消息。
苹果充电器。

总结

抽象工厂模式中,可存在多个方法用于创建同品类的不同对象。然而,该模式存在一定的缺点,即当需要增加新的品类时,需要对所有的具体工厂实现类进行修改。例如,若在抽象工厂接口AbstractFactory中新增一个方法B,那么AndroidFactoryIPhoneFactory等具体工厂类都需要实现这个新增的方法B

抽象工厂模式中,可以将上述示例中的工厂替换为工作中实际会用到的对象,例如对象存储服务(OSS)。以下展示的仅仅是两个 OSS 工厂,而在实际应用中,可能需要对接众多的 OSS 服务提供商,以满足不同的业务需求。
在这里插入图片描述


http://www.ppmy.cn/embedded/102840.html

相关文章

4.sklearn-K近邻算法、模型选择与调优

文章目录 环境配置(必看)头文件引用1.sklearn转换器和估计器1.1 转换器 - 特征工程的父类1.2 估计器(sklearn机器学习算法的实现) 2.K-近邻算法2.1 简介:2.2 K-近邻算法API2.3 K-近邻算法代码2.4 运行结果2.5 K-近邻算法优缺点 3.模型选择与调…

Android wifi主要广播详解

Android wifi相关主要广播总结 文章目录 Android wifi相关主要广播总结一、前言二、wifi主要广播分析介绍1、简单的广播监听2、wifi 广播对应的 action 字符串:3、广播和接收的相关数据 三、广播相关日志:1、wifi扫描广播的日志2、wifi连接、断开的日志&…

C语言sprintf函数使用

1 其函数原型为:int sprintf(char *str, const char *format,...)。 具体用法如下: 基本语法: str:目标字符串的指针,用于存储格式化后的结果。format:格式化字符串,用于指定输出的格式。后续是…

国外课程环境总结

CS106L2023 and CS106B 环境配置(详细教程)_cs106b 2023-CSDN博客

DNS工作流程

DNS(域名系统) DNS(域名系统)的工作流程是将人类可读的域名(如 www.example.com)转换为机器可读的IP地址(如 192.0.2.1),以便计算机能够相互通信。DNS查询过程可以分为递…

如何构建基于Java SpringBoot的保险业务管理与数据分析系统

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

Linux下IO多路复用—select,poll,epoll

一.概述 1.IO多路复用介绍 IO多路复用是一种操作系统的技术,用于在单个线程或进程中管理多个输入输出操作。它的主要目的是通过将多个IO操作合并到一个系统调用中来提高系统的性能和资源利用率,避免了传统的多线程或多进程模型中因为阻塞IO而导致的资源…

在Docker中,本地的镜像文件都存放在哪里?

在Docker中,本地的镜像文件存放位置取决于您所使用的操作系统。以下是一些常见操作系统中Docker镜像的默认存放位置: 1. Linux系统 默认存放位置:在Linux系统中,Docker镜像默认存放在/var/lib/docker/目录下。具体来说&#xff…

SVN项目的文件泄露分析和漏洞修复

说明:本文仅是用于学习分析自己搭建的SVN漏洞内容和原理,请勿用在非法途径上,违者后果自负,与笔者无关;本文开始前请认真详细学习《‌中华人民共和国网络安全法》‌及其相关法规内容【学法时习之丨网络安全在身边一图了解网络安全法_中央网络安全和信息化委员会办公室】 …

文件IO函数练习

作业&#xff1a;使用write和read完成文件的拷贝。 代码 #include <myhead.h>int main(int argc, const char *argv[]) {int fd open("./1.txt",O_RDONLY);//已只读打开被拷贝文件if(-1 fd){perror("open");return -1;}int fd1 open("./2.…

docker-compose 搭建redis集群(三台服务器,每台服务器上一主一从)

文章目录 1、前言2、准备工作2.1、Docker 安装2.2、docker-compose 安装2.3 目录创建 3、redis配置文件3.1 生成redis.conf文件3.2 shell文件无法执行问题解决3.3 docker-compose.yml文件编写 4、启动集群及初始化4.1 启动 Redis 集群4.2 Redis 集群初始化 5、总结 1、前言 使用…

【Tesla FSD V12的前世今生】从模块化设计到端到端自动驾驶技术的跃迁

自动驾驶技术的发展一直是全球汽车行业的焦点&#xff0c;Tesla的Full-Self Driving&#xff08;FSD&#xff09;系统凭借其持续的技术革新和强大的数据支持&#xff0c;在这个领域独占鳌头。本文将深入介绍Tesla FSD V12的演进历史&#xff0c;从自动驾驶的基础概念入手&#…

Golang基础1

Golang 教程1 注意&#xff0c;该文档只适合有编程基础的同学&#xff0c;这里的go教程只给出有区别的知识点 [go教程](‘Go基础 Go语言中文文档 (topgoer.com)’) 一、基础 tip: 下划线_ 可以起到忽略的作用 运行以下命令来初始化一个新的 Go 模块&#xff1a; // 如你…

pdf、mp4、zip、rar、jpg等文件被大量访问造成流量超标解决方案

1.现象 网站流量使用突然暴涨几十、几百倍&#xff0c;访问量也明显暴增&#xff0c;单天流量使用达到几十、几百G&#xff0c;造成虚拟主机因流量超标被停止。如图&#xff1a; 2.分析 经分析 访问统计报表 &#xff0c;主要是某个或几个大文件被大量访问&#xff0c;从造成…

elasticsearch安装在服务器并进行向量检索

服务器安装elasticsearch 安装Elasticsearch的步骤通常包括以下几个阶段&#xff1a; 导入Elasticsearch公钥。 创建Elasticsearch仓库。 安装Elasticsearch。 启动Elasticsearch服务。 配置Elasticsearch开机自启。 以下是针对基于Debian/Ubuntu系统的安装示例&#xf…

ubuntu 更新网卡丢失

查看网卡设备信息 sudo lshw -c Network设备 处于 description: Ethernet interface rootlinuxling-MoreFine-S500:/etc/netplan# sudo lshw -c Network*-networkdescription: Ethernet interfaceproduct: RTL8125 2.5GbE Controllervendor: Realtek Semiconductor Co., Ltd.…

【kubernetes】kubernetes Deployment 详解

Deployment 详解 kubernetes Deployment 详解更新/回滚/缩放/暂停/恢复部署操作 发布策略1、在zs命名空间下创建3个httpd副本并查看结果2、尝试删除其中一个副本并查看结果3、删除所有副本并查看结果4、使用k8s做金丝雀发布测试 kubernetes Deployment 详解 更新/回滚/缩放/暂…

在Ubuntu24.04上安装多主多从的高可用Kubernetes节点

前言 因为镜像拉取失败&#xff0c;所以好多小伙伴说calico网络插件安装失败。之前写过CentOS7上安装多主节点的Kubernets集群&#xff0c;而CentOS7又停止维护了。所以借着在Ubuntu上安装多主的Kubernetes集群的机会&#xff0c;使用国内镜像安装calico网络插件。 视频教程地…

Vue3-win7搭建vue3环境

Vue3-win7搭建vue3环境 官方要求的信息是是node.js 18.03以上。而我的环境&#xff1a;win7 x64&#xff0c; vscode 1.34。 参考网址&#xff1a; 0、基本的安装 https://blog.csdn.net/m0_49139268/article/details/126159171 a、这里有各种安装包的下载路径&#xff08;镜…

外包干了两年,快要废了。。。

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 简单的说下&#xff0c;我大学的一个同学&#xff0c;毕业后我自己去了自研的公司&#xff0c;他去了外包&#xff0c;快两年了我薪资、技术各个方面都有了很大的…