四、 简单工厂模式

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 Drink 抽象类
    • 2.2 Tea 类
    • 2.3 Coffee 类
    • 2.4 DrinkFactory 类
    • 2.5 Client 类
    • 2.6 Client 类运行结果
    • 2.7 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 Product ( 抽象产品 )
      • 3.1.2 ConcreteProduct ( 具体产品 )
      • 3.1.3 Factory ( 工厂 )
      • 3.1.4 Client ( 客户端 )
    • 3.2 类图
  • 4 注意事项
  • 5 在源码中的使用
  • 6 优缺点
  • 7 使用场景
    • 7.1 对象的 创建逻辑相对简单
    • 7.2 对象的 创建过程相对稳定
    • 7.3 需要根据 不同条件 创建 不同实例
    • 7.4 减少 客户端代码 与 具体类 的耦合
    • 7.5 产品类型较少 且 创建逻辑简单
    • 7.6 总结
  • 8 总结


1 基本介绍

简单工厂模式(Simple Factory Pattern),又称 静态工厂方法模式(Static Factory Method Pattern),是一种 创建型 设计模式。它通过一个专门的 工厂类负责创建其他类的实例,这些被创建的实例通常都具有 共同的父类或接口

注意:简单工厂模式虽然 不属于GOF(四人组)提出的 23 种经典设计模式之一,但它仍然是一种 常见且有用的 设计模式,特别适用于一些 简单的创建对象 的场景。它就像被移出太阳系八大行星的矮行星——冥王星。

2 案例

案例中的类如下所示:

  • 饮品类:Drink 抽象类,它有一个抽象方法 drink(),并有两个子类:Tea 类和 Coffee 类。
  • 饮品工厂类:DrinkFactory 类有一个静态方法,能够根据饮品的类型生产对应的饮品。
  • 客户端类:Client 类中使用了饮品工厂,创建了不同类型的饮品。

2.1 Drink 抽象类

public abstract class Drink { // 饮品的抽象类public abstract void drink(); // 喝饮品protected String name; // 饮品的名称public Drink(String name) {this.name = name;}public String getName() {return name;}
}

2.2 Tea 类

public class Tea extends Drink { // 茶饮品public Tea(String name) {super(name);}@Overridepublic void drink() {System.out.println("你喝了[" + super.name + "]这个茶饮品");}
}

2.3 Coffee 类

public class Coffee extends Drink { // 咖啡饮品public Coffee(String name) {super(name);}@Overridepublic void drink() {System.out.println("你喝了[" + super.name + "]这个咖啡饮品");}
}

2.4 DrinkFactory 类

public class DrinkFactory { // 饮品工厂/*** 根据 饮品类型 创建 指定名称 的饮品* @param drinkType 饮品类型* @param drinkName 指定名称* @return 创建的饮品*/public static Drink createDrink(String drinkType, String drinkName) {Drink drink = null;switch (drinkType) {case "tea": // 如果是茶drink = new Tea(drinkName); // 则创建 茶饮品 的对象break;case "coffee": // 如果是咖啡drink = new Coffee(drinkName); // 则创建 咖啡饮品 的对象break;default: // 什么也不需要做,等待返回 null}return drink;}
}

2.5 Client 类

public class Client { // 使用 饮品工厂创建具体饮品 的客户端public static void main(String[] args) {Drink tea = DrinkFactory.createDrink("tea", "铁观音");tea.drink();Drink coffee = DrinkFactory.createDrink("coffee", "拿铁");coffee.drink();}
}

2.6 Client 类运行结果

你喝了[铁观音]这个茶饮品
你喝了[拿铁]这个咖啡饮品

2.7 总结

如果想要再添加一种饮品,则只需要再写一个 Drink 的子类,并在 DrinkFactory 类的 createDrink() 方法的 switch 语句中再添加一个分支即可。从而 在一定程度上 增强了系统的扩展性

Client 类不需要知道具体的饮品是如何创建的,即使 DrinkFactory 类的 createDrink() 方法被修改了,也不会影响 Client 类已有的代码。从而 减少了 客户端 与 具体饮品 的耦合

3 各角色之间的关系

3.1 角色

3.1.1 Product ( 抽象产品 )

该角色负责 定义 所有具体产品的 共性(包括 属性 和 方法),是所有具体产品的 父类 或 被实现的接口。在本案例中,Drink 抽象类扮演了这个角色。

3.1.2 ConcreteProduct ( 具体产品 )

该角色负责 实现 抽象产品所定义的 方法。在本案例中,Tea 类和 Coffee 类都扮演了这个角色。

3.1.3 Factory ( 工厂 )

该角色负责 实现创建 所有 具体产品的内部逻辑,并 对外提供一个用于创建产品对象的接口。在本案例中,DrinkFactory 类扮演了这个角色。

3.1.4 Client ( 客户端 )

该角色负责 使用 工厂创建具体产品。在本案例中,Client 类扮演了这个角色。

3.2 类图

alt text
在本类图中,Product抽象类,实际上,Product 还可以是 接口,它的作用是:记录具体产品的共性。

注意:一般来说,Factory 类的 createProduct() 方法是 静态的,这就是 静态工厂方法模式 的出处。

4 注意事项

  • 不适合复杂系统:对于 产品类型较多创建逻辑复杂 的系统,简单工厂模式可能不是最佳选择。在这种情况下,可以考虑使用 工厂方法模式抽象工厂模式 等更灵活的设计模式。
  • 难以测试:由于工厂类通常包含多个分支语句来创建不同类型的对象,所以测试人员需要 确保每个分支都被正确测试,以确保工厂类的正确性。
  • 扩展具体产品:每当需要添加新的产品时,都需要修改工厂类中的代码(如增加新的分支语句)。
  • 工厂类的职责:确保工厂类的职责保持 简单清晰。工厂类应该 只负责创建对象,而不应该包含其他复杂的业务逻辑。
  • 考虑使用接口:在可能的情况下,将抽象产品角色定义为 接口 而不是 抽象类,可以提高系统的灵活性和扩展性。在 Java 中,一个类只能继承一个父类,但能实现多个接口。
  • 配置和依赖注入:在某些情况下,可以考虑使用 配置和依赖注入(DI)来替代简单工厂模式。通过配置和 DI,可以在不修改代码的情况下更改对象的创建方式,从而提高系统的灵活性和可维护性。

5 在源码中的使用

在 JDK 中,简单工厂模式被广泛应用于各种类的设计中。例如,java.util.Calendar 类就使用了简单工厂模式。通过 Calendar.getInstance() 静态方法,可以获取到当前默认时区和默认语言环境的 Calendar 对象。这个方法的内部调用了 createCalendar() 方法,createCalendar() 方法使用了简单工厂模式,如下所示:

// 在 java.util.Calendar 类中
public static Calendar getInstance() {Locale aLocale = Locale.getDefault(Locale.Category.FORMAT);return createCalendar(defaultTimeZone(aLocale), aLocale);
}
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();if (provider != null) {try {return provider.getInstance(zone, aLocale);} catch (IllegalArgumentException iae) {// fall back to the default instantiation}}// 注意:在以下的代码中使用到了简单工厂模式Calendar cal = null;if (aLocale.hasExtensions()) {String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype != null) {cal = switch (caltype) {case "buddhist" -> new BuddhistCalendar(zone, aLocale);case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);case "gregory"  -> new GregorianCalendar(zone, aLocale);default         -> null;};}}if (cal == null) {if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {cal = new BuddhistCalendar(zone, aLocale);} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"&& aLocale.getCountry() == "JP") {cal = new JapaneseImperialCalendar(zone, aLocale);} else {cal = new GregorianCalendar(zone, aLocale);}}return cal;
}

6 优缺点

优点

  • 降低系统的耦合度:客户端无需知道具体产品的 创建细节,只需传入参数即可获得所需对象。
  • 增加系统的部分扩展性:如果想要新增一个产品,则只需在工厂类中增加这个产品创建的代码,但不能扩展过多的产品,否则会导致工厂类难以维护。

缺点

  • 违背开闭原则:增加新产品时需要修改工厂类中创建产品的方法,而不是新增一个方法,违背了 开闭原则(对扩展开放,对修改关闭)。
  • 产品类型过少:当产品类型较多时,工厂类的代码会变得很复杂,其职责会过于沉重,难以维护。
  • 创建逻辑过于简单:当产品的创建逻辑很复杂时,工厂类的代码会变得很复杂,难以维护。

7 使用场景

7.1 对象的 创建逻辑相对简单

  • 场景:对象的 创建逻辑相对简单,没有太多复杂的 依赖关系初始化过程
  • 示例:如创建一个图形绘制工具,可以绘制不同类型的图形(如圆形、矩形等),每种图形的创建逻辑相对简单,且不需要复杂的依赖关系。

7.2 对象的 创建过程相对稳定

  • 场景:对象的 创建过程相对稳定,不会经常变化,从而减少对工厂类的修改,方便管理和维护。
  • 实例:在数据库连接管理中,如果数据库类型相对稳定(如 MySQL、Oracle 等),可以使用简单工厂模式根据配置或用户输入的数据库类型来创建相应的数据库连接对象。

7.3 需要根据 不同条件 创建 不同实例

  • 场景:在需要 根据不同的条件或参数来创建不同类型的对象 时,简单工厂模式可以根据传入的参数 动态地 创建并返回相应的对象实例。
  • 实例:日志记录器可以根据用户的选择或配置来创建不同类型的日志记录器对象(如文件日志记录器、数据库日志记录器等),并将日志记录到不同的目标中。

7.4 减少 客户端代码 与 具体类 的耦合

  • 场景:简单工厂模式通过将对象的创建逻辑 封装 在工厂类中,客户端只需要通过工厂类来获取所需的对象,而不需要知道对象的具体创建过程,从而 减少了 客户端代码 与 具体类 之间的 耦合
  • 实例:在文件解析器中,根据文件类型(如 XML 文件、JSON 文件等)来创建相应的文件解析器对象,客户端只需要通过工厂类传入文件类型即可获取相应的解析器对象,而不需要关心具体的解析逻辑。

7.5 产品类型较少 且 创建逻辑简单

  • 场景:简单工厂模式适用于 产品类型相对较少创建逻辑相对简单 的场景。如果 产品类型较多创建逻辑复杂,则 只能 使用更灵活的 工厂方法模式抽象工厂模式
  • 实例:如一个简单的文具工厂,只需要生产铅笔、钢笔和毛笔等少数几种产品,且每种产品的创建逻辑相对简单。

7.6 总结

综上所述,简单工厂模式适用于对象 创建逻辑简单创建过程稳定需要根据不同条件创建不同实例 以及 减少客户端代码与具体类耦合 的场景。然而,简单工厂模式也存在一些 局限性,如 工厂类职责过重不利于系统扩展 等,因此在具体使用时,需要根据实际情况决定是否使用简单工厂模式。

8 总结

简单工厂模式是一种 创建型 设计模式,它通过一个工厂类来创建具体产品,这些具体产品通常具有共同的抽象产品作为父类或接口。

简单工厂模式将具体产品的创建逻辑 封装 在工厂类中,客户端无需知道具体产品的类名,只需要知道相应的参数即可,从而 降低了 客户端与具体产品类之间的 耦合度

简单工厂模式要求 具体产品的种类不多,并且具体产品的 创建逻辑比较简单

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

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

相关文章

[译] Rust项目的基础设施

本篇是对 RustConf 2023中的Infrastructure for Rust这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. 我今天要和大家讨论支持Rust及Rust项目的基础设施。Rust是一门令人惊叹的语言,我非常喜欢它,看到它的普及度和社区的成长令人非常满意。但从项…

CSA笔记4-包/源管理命令以及本地光盘仓库搭建

包/源管理命令 1.rpm是最基础的rmp包的安装命令,需要提前下载相关安装包和依赖包 2.yum/dnf是基于rpm包的自动安装命令,可以自动在仓库中匹配安装软件和依赖包 注意:以上是安装命令,以下是安装源 3.光盘源:是指安装系统时后的…

JAVA零基础学习2(算术逻辑三元运算符、原码反码补码、标准的类如何描写)

JAVA零基础学习2(算术逻辑三元运算符、原码反码补码、标准的类如何描写) 算术运算符算术运算符自增和自减运算符算术运算符的优先级示例代码 逻辑运算符三元运算符示例代码示例1:简单的条件判断示例2:嵌套的三元运算符 原码反码补…

【Elasticsearch7.11】集合字段 数量大小查询

需求: 有个字符串集合字段,需要查询出 集合大小 大于等于2 的数据 1、字段mapping "belong_account": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart&qu…

ClusterIP、NodePort、LoadBalancer 和 ExternalName

Service 定义 在 Kubernetes 中,由于Pod 是有生命周期的,如果 Pod 重启它的 IP 可能会发生变化以及升级的时候会重建 Pod,我们需要 Service 服务去动态的关联这些 Pod 的 IP 和端口,从而使我们前端用户访问不受后端变更的干扰。 …

MySQL运维实战之ProxySQL(9.10)proxysql监控

作者:俊达 stats数据库 从stats数据库中可以查到proxysql一些内部组件的状态,如内存使用情况、连接池信息、会话信息、SQL执行状态等。 mysql> show tables from stats; --------------------------------------- | tables …

昇思25天学习打卡营第11天 | ResNet50迁移学习

利用ResNet50进行迁移学习的探索与实践 在人工智能的各个领域中,迁移学习已成为一个极其有效的策略,特别是在图像识别任务中。通过使用预训练模型,我们可以利用在大型数据集(如ImageNet)上学到的丰富特征,…

Vue 使用 Element UI 组件库

https://andi.cn/page/621589.html

C嘎嘎类与对象拓展

本篇文章是对浅谈C嘎嘎类与对象的拓展 初始化列表(初始化) 格式:类名(类型 形参1, 类型 形参2) :(冒号) 变量【类中声明的变量】(形参1) , 变量【类中…

基于天地图使用Leaflet.js进行WebGIS开发实战

目录 前言 一、天地图的key 1、创建应用 2、调用限制策略 注: (1)日服务调用超量会暂时拒绝访问,次日自动开启; (2)如果服务调用过程中存在数据爬取或者下载行为,服务调用会被自…

【中项】系统集成项目管理工程师-第3章 信息技术服务-3.6服务发展

前言:系统集成项目管理工程师专业,现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试,全称为“全国计算机与软件专业技术资格(水平)考试”&…

K12智慧校园智能化解决方案

1. 项目背景 “十三五”期间,教育信息化工作旨在为教育改革发展提供动力与手段,目标是到2020年建成与国家教育现代化发展目标相适应的教育信息化体系。 2. 建设需求 智慧校园系统框架解析与建设目标分析,旨在实现教育信息化目标任务。 3.…

【C++航海王:追寻罗杰的编程之路】关于空间配置器你知道多少?

目录 1 -> 什么是空间配置器 2 -> 为什么需要空间配置器 3 -> SGI-STL空间配置器的实现原理 3.1 -> 一级空间配置器 3.2 -> 二级空间配置器 3.2.1 -> 内存池 3.2.2 -> SGI-STL中二级空间配置器设计 3.2.3 -> SGI-STL二级空间配置器之空间申请 …

Java | Leetcode Java题解之第263题丑数

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isUgly(int n) {if (n < 0) {return false;}int[] factors {2, 3, 5};for (int factor : factors) {while (n % factor 0) {n / factor;}}return n 1;} }

C++树形结构(3 树的中心、重心)

目录 一.树的中心&#xff1a; 1.树的概念&#xff1a; 2.树的性质&#xff1a; 性质1&#xff1a; 性质2&#xff1a; 3.树的中心求解&#xff1a; 4.例题&#xff1a; 二.树的重心&#xff1a; 1.基础概念&#xff1a; 2.求解方法&#xff1a; 3.例题&#xff1a;…

毕业/期刊论文发表必备:YOLOv5 / v7 / v8 /v10算法网络结构图【文末提供原型文件下载地址】

前言:Hello大家好,我是小哥谈。同学们在写YOLO算法相关毕业论文/期刊论文的时候,不可避免的会用到相关版本的网络结构图,曾有很多小伙伴私信我索要原型文件,本文就给大家提供YOLOv5/v7/v8/v10版本算法网络结构图及原型文件下载地址。🌈 目录 🚀1.网络结构图 �…

SpringBoot源码深度解析

今天&#xff0c;聊聊SpringBoot的源码&#xff0c;本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2&#xff0c;可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系&#xff0c;因为版本越新&#xff0c;新增的功能越多&#xff0c;反而对Spri…

2024-07-19 Unity插件 Odin Inspector9 —— Validation Attributes

文章目录 1 说明2 验证特性2.1 AssetsOnly / SceneObjectsOnly2.2 ChildGameObjectsOnly2.3 DisallowModificationsIn2.4 FilePath2.5 FolderPath2.6 MaxValue / MinValue2.7 MinMaxSlider2.8 PropertyRange2.9 Required2.10 RequiredIn2.11 RequiredListLength2.12 ValidateIn…

Python数据风险案例54——人工智能热门概念股爬虫分析其价值(三因子模型)

案例背景 人工智能概念如火如荼的夏天&#xff0c;在这个2024年&#xff0c;我觉得需要提早布局一下这个概念。所以我们找一下A股里面人们的人工智能概念股&#xff0c;然后分析他们的数据应用三因子模型&#xff0c;也就是最经典的资本资产定价模型的衍生版去研究他们各自的投…

51 单片机[8]:串口通信

一、目标 单片机通过串口向电脑发送数据&#xff08;数字递增&#xff09;电脑通过串口控制单片机上的LED 二、基本概念 1. 串口 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。 单片机的串口可以…