五、工厂方法模式

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 Drink 抽象类
    • 2.2 Tea 类
    • 2.3 Coffee 类
    • 2.4 DrinkFactory 抽象类
    • 2.5 TeaFactory 类
    • 2.6 CoffeeFactory 类
    • 2.7 Client 类
    • 2.8 Client 类运行结果
    • 2.9 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 Product ( 抽象产品 )
      • 3.1.2 ConcreteProduct ( 具体产品 )
      • 3.1.3 Factory ( 抽象工厂 )
      • 3.1.4 ConcreteFactory ( 具体工厂 )
      • 3.1.5 Client ( 客户端 )
    • 3.2 类图
  • 4 注意事项
  • 5 在源码中的使用
  • 6 优缺点
  • 7 适用场景
  • 8 总结


1 基本介绍

工厂方法模式(Factory Method Pattern)是一种 创建型 设计模式,它 定义了一个创建对象的 接口(抽象方法),但 让子类决定要实例化的类是哪一个

它不同于 简单工厂模式,工厂方法模式将对象的创建逻辑分散到各个具体的工厂类中,而不是集中在一个工厂类中。

它还不同于 抽象工厂模式,工厂方法模式在创建产品对象时有 相似的额外操作,所以需要给外界提供一个方法,并强制子类实现另一个方法。

2 案例

本模式的案例 和 简单工厂模式 的案例很相似,只不过将其中的单个工厂改为了 一个抽象工厂类 + 多个工厂实现类。如下所示:

  • 饮品:Drink 抽象类有一个抽象方法 drink(),代表喝这个饮品。它有两个子类:Tea 类和 Coffee 类。
  • 工厂:DrinkFactory 抽象类有两个方法:第一个方法是获取已创建的饮品数,第二个方法能够根据饮品的类型生产对应的饮品。它有两个实现类,分别为 TeaFactory 类和 CoffeeFactory 类。
  • 客户端: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);
    }

    @Override
    public void drink() {
        System.out.println("你喝了[" + super.name + "]这个茶饮品");
    }
}

2.3 Coffee 类

public class Coffee extends Drink { // 咖啡饮品
    public Coffee(String name) {
        super(name);
    }

    @Override
    public void drink() {
        System.out.println("你喝了[" + super.name + "]这个咖啡饮品");
    }
}

2.4 DrinkFactory 抽象类

public abstract class DrinkFactory  { // 饮品工厂抽象类
    private static final AtomicInteger count = new AtomicInteger(0); // 统计创建的饮品个数

    // 获取创建的饮品个数
    public static int getDrinkCount() {
        return count.get();
    }

    // 创建 指定名称 的饮品,被外部的类调用
    public final Drink create(String drinkName) {
        count.getAndIncrement();
        return createDrink(drinkName);
    }

	// 创建 指定名称 的饮品,留给子类实现,不应该被外部的类调用
    protected abstract Drink createDrink(String drinkName);
}

2.5 TeaFactory 类

public class TeaFactory extends DrinkFactory { // 茶饮品工厂类
    @Override
    protected Drink createDrink(String drinkName) {
        return new Tea(drinkName); // 创建 茶饮品 的对象
    }
}

2.6 CoffeeFactory 类

public class CoffeeFactory extends DrinkFactory { // 咖啡饮品工厂类
    @Override
    protected Drink createDrink(String drinkName) {
        return new Coffee(drinkName); // 创建 咖啡饮品 的对象
    }
}

2.7 Client 类

// 最好让 Client 与 DrinkFactory 在不同的包下,这样可以避免 Client 误调用 createProduct() 方法
public class Client { // 使用 具体饮品工厂创建具体饮品 的客户端
    public static void main(String[] args) {
        DrinkFactory teaFactory = new TeaFactory();
        Drink tea = teaFactory.create("铁观音");
        tea.drink();

        DrinkFactory coffeeFactory = new CoffeeFactory();
        Drink coffee = coffeeFactory.create("拿铁");
        coffee.drink();

        System.out.println("已经创建了 " + DrinkFactory.getDrinkCount() + " 个饮品");
    }
}

2.8 Client 类运行结果

你喝了[铁观音]这个茶饮品
你喝了[拿铁]这个咖啡饮品
已经创建了 2 个饮品

2.9 总结

在本模式的案例中,工厂类比 简单工厂模式 多;在初始化工厂时,需要初始化具体工厂的对象;在生产产品时,只需要传入与对象有关的参数,不需要传入与对象类型有关的信息。

如果想要新增一种具体产品,则只需写 具体产品类其对应的具体工厂类 的代码,而不需要修改 抽象工厂类,这样就 遵守了开闭原则提高了系统的扩展性

此外,本模式还有简单工厂模式的优点——降低系统之间的耦合度,这是因为在本模式中,客户端也不需要知道具体产品的创建逻辑,只要会使用工厂即可。

3 各角色之间的关系

3.1 角色

3.1.1 Product ( 抽象产品 )

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

3.1.2 ConcreteProduct ( 具体产品 )

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

3.1.3 Factory ( 抽象工厂 )

该角色负责 定义 创建产品对象的 抽象方法,并 统一处理创建产品对象的额外操作。在本案例中,DrinkFactory 抽象类扮演了这个角色。

3.1.4 ConcreteFactory ( 具体工厂 )

该角色负责 实现 创建产品对象的 抽象方法,而且只负责创建某一种具体产品。在本案例中,TeaFactory 类和 CoffeeFactory 类都在扮演这个角色。

3.1.5 Client ( 客户端 )

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

3.2 类图

alt text
说明:ConcreteProduct 类和 ConcreteFactory 类是一一对应的,它们两个的关系和 Product 抽象类与 Factory 抽象类的关系是一致的。此外,工厂的 createProduct() 方法的访问权限是 protected,即仅允许 本包的类 或 子类 使用,并且被 abstract 修饰,强制子类重写此方法;create() 方法的访问权限是 public,所以可以被外部的类使用。

4 注意事项

本模式的注意事项与 抽象工厂模式 的十分相似,共有以下几点:

  • 考虑系统复杂度:使用工厂方法模式需要引入一个或多个工厂类,这可能会增加系统的复杂度,在决定是否使用此模式时,需要权衡其带来的好处和增加的复杂度。
  • 共同基类或接口:所有产品类必须有一个共同的基类或接口,以便 create() 方法能够返回一个 通用 的产品类型,这有助于保持系统的灵活性和可扩展性。
  • 对象重用:在某些情况下,为了 提高性能减少资源消耗create() 方法可能会返回 缓存的对象对象池中的对象,而不是每次都创建新对象,这需要根据具体的应用场景和需求来决定。
  • 扩展具体产品:当需要新增产品时,只需新增产品类和相应的工厂类。
  • 考虑使用接口:在可能的情况下,将抽象产品角色定义为 接口 而不是 抽象类,可以提高系统的灵活性和扩展性。在 Java 中,一个类只能继承一个父类,但能实现多个接口。
  • 考虑使用抽象工厂模式:如果在创建产品对象时有相似的额外操作,则使用本模式比较好,但是,如果没有相似的额外操作,最好使用 抽象工厂模式。

5 在源码中的使用

在 Spring 框架中,工厂模式被广泛应用于对象的创建和管理过程中。

Spring 的 AbstractApplicationContext 抽象类是工厂方法模式的典型应用,它 定义 了多个 getBean() 方法,允许客户端通过传入不同的条件(如 Bean 的名称或类型)来获取对应的 Bean 实例。

// 在 org.springframework.context.support.AbstractApplicationContext 抽象类中有如下几个方法
@Override
public Object getBean(String name) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name);
}

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name, requiredType);
}

@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(requiredType);
}

// 检查 Bean 是否活跃
protected void assertBeanFactoryActive() {
	if (!this.active.get()) {
		if (this.closed.get()) {
			throw new IllegalStateException(getDisplayName()
					+ " has been closed already");
		} else {
			throw new IllegalStateException(getDisplayName()
					+ " has not been refreshed yet");
		}
	}
}

@Override
public abstract ConfigurableListableBeanFactory getBeanFactory()
				throws IllegalStateException;

这三个 getBean() 方法都检查 Bean 是否活跃(额外操作),并且还调用了 abstract 方法 getBeanFactory(),这个方法由其子类实现,例如 AbstractRefreshableApplicationContext 抽象类。

6 优缺点

优点

  • 扩展性好:当需要新增产品时,只需新增产品类和相应的工厂类,无需修改原有的工厂抽象类和工厂类,满足开闭原则。
  • 职责单一:每个工厂类只负责创建对应的产品,满足单一职责原则。
  • 降低耦合度:客户端通过工厂抽象类与具体产品解耦,不需要知道产品的具体实现细节,只需要知道如何获取所需的产品。
  • 灵活性高:可以根据不同的应用场景或条件选择不同的工厂子类来创建产品,增加了系统的灵活性。

缺点

  • 增加了系统的复杂度:每增加一种产品,就需要增加一个对应的产品类和一个对应的工厂子类,这会增加系统中类的数量,从而增加系统的复杂度。
  • 增加了系统的抽象层次:工厂方法模式引入了 工厂抽象类 和 工厂子类,增加了系统的抽象层次,使得系统的理解和维护变得 相对 复杂。
  • 过度设计:在某些情况下,如果系统中产品的 种类不多,或者产品之间的 差异性不大,使用工厂方法模式可能会导致过度设计,增加了不必要的复杂性。这时使用 简单工厂模式 会好一点。
  • 依赖性问题:如果工厂类依赖于其他资源或配置,那么这些依赖项的管理和配置可能会变得复杂。特别是在大型系统中,工厂类的依赖关系可能会形成一个复杂的网络,增加了系统的维护难度。

7 适用场景

  • 多种产品族的创建:当系统中存在多个产品族,并且这些产品族之间存在公共的接口或抽象类,但每个产品族的具体实现不同时,可以使用工厂方法模式。每个产品族对应一个工厂类,负责创建该族中的具体产品对象。
  • 系统需要扩展但不想修改已有代码:当系统需要添加新的产品类型时,如果希望在不修改已有代码的基础上实现扩展,工厂方法模式是一个很好的选择。
  • 复杂的对象创建:当对象的创建过程比较复杂,包含多个步骤或者需要依赖其他对象时,使用工厂方法模式可以将对象的创建过程封装在工厂类中,使得客户端代码更加简洁和易于理解。
  • 隐藏具体产品的创建细节:在某些情况下,我们可能不希望客户端代码知道具体产品的创建细节,或者希望 隐藏 这些细节以简化客户端的使用。工厂方法模式可以通过提供一个共同的接口来隐藏具体产品的创建细节,使得客户端只需通过接口来创建对象。
  • 使用第三方类库:当系统需要使用第三方类库来创建对象,并且这些对象的创建过程比较复杂或者需要遵循特定的规则时,可以使用工厂方法模式来封装这些创建过程,以便在系统中更加方便地使用这些对象。
  • 设计考虑:在系统设计初期,如果预见到将来可能需要添加新的产品类型或者对产品类型进行扩展,可以考虑使用工厂方法模式来为未来可能的扩展 预留接口。这样可以使得系统在扩展时更加灵活和方便。
  • 在创建产品对象时有相似的额外操作:如果创建产品对象时有相似的额外操作,那么就使用工厂方法模式,将相似的额外操作放到 提供给外界的 创建产品对象方法中,让工厂子类只实现初始化产品对象的逻辑。

8 总结

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

工厂方法模式将具体产品的创建逻辑 封装 在各个工厂类中,并 将相 似的额外操作 封装到对外提供的方法中,客户端无需知道具体产品的类名,只需要知道相应的工厂即可,从而 降低了 客户端与具体产品类之间的 耦合度

与简单工厂模式相比,工厂方法模式对具体产品的 种类不做限制,并且具体产品的 创建逻辑也可以很复杂 。在扩充产品类型时,该模式不需要修改代码,只需要添加代码,满足 开闭原则。每个具体工厂类只负责创建一种产品,满足 单一职责原则

与抽象工厂模式相比,工厂方法模式 处理了创建产品对象时相似的额外逻辑,但是在 Java 中,由于单继承,所以在 灵活性 方面,使用抽象类(工厂方法模式) 比 使用接口(抽象工厂模式) 差一些

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何开启或者关闭 Windows 安全登录?

什么是安全登录 什么是 Windows 安全登录呢&#xff1f;安全登录是 Windows 附加的一个组件&#xff0c;它可以在用户需要登录的之前先将登录界面隐藏&#xff0c;只有当用户按下 CtrlAltDelete 之后才出现登录屏幕&#xff0c;这样可以防止那些模拟登录界面的程序获取密码信息…

【Apache Doris】数据副本问题排查指南

【Apache Doris】数据副本问题排查指南 一、问题现象二、问题定位三、问题处理 本文主要分享Doris中数据副本异常的问题现象、问题定位以及如何处理此类问题。 一、问题现象 问题日志 查询报错 Failed to initialize storage reader, tablet{tablet_id}.xxx.xxx问题说明 查…

SSD基本架构与工作原理

SSD的核心由一个或多核心的CPU控制器、DRAM缓存以及多个NAND闪存芯片组成。CPU控制器负责管理所有读写操作&#xff0c;并通过DRAM缓存存储映射表等元数据&#xff0c;以加速寻址过程。 NAND闪存则是数据存储的实际介质&#xff0c;其组织结构从大到小依次为通道&#xff08;包…

Springboot项目远程部署gitee仓库(docker+Jenkins+maven+git)

创建仓库 创建一个Springboot项目&#xff0c;勾选web将该项目创建git本地仓库&#xff0c;再创建远程仓库推送上去 创建TestController RestControllerRequestMapping("/test")public class TestController {GetMapping("/hello")public String sayHell…

LeetCode 热题 HOT 100 (001/100)【宇宙最简单版】

【链表】 No. 0160 相交链表 【简单】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#x…

【系统架构设计 每日一问】四 如何对关系型数据库及NoSql数据库选型

根据不同的业务需求和场景&#xff0c;选择适合的数据库类型至关重要。以下是一个优化后的表格展示&#xff0c;涵盖了管理型系统、大流量系统、日志型系统、搜索型系统、事务型系统、离线计算和实时计算七大类业务系统的数据库选型建议。先明确下NoSQL的分类 NoSQL数据库分类…

RabbitMQ_基础篇

文章目录 第一章 消息中间件1.1 应用场景1.2 常用消息中间件1.2.1 ActiveMQ1.2.2 RabbitMQ1.2.3 RocketMQ1.2.4 Kafka 第二章 RabbitMQ2.1 为什么选择RabbitMQ2.2 RabbitMQ简介2.3 RabbitMQ架构2.4 RabbitMQ工作模式2.4.1 Hello World2.4.2 Work Queues2.4.3 Publish/Subscribe…

分离式网络变压器的集成化设计替代传统网络变压器(网络隔离滤波器)尝试

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;今天分享的是应用了分离式网络变压器设计的的新型网络变压器&#xff08;网络隔离变压器&#xff09; 今天我们一起来看这款新型网络变压器&#xff0c;它就是应用分离式网络变压器集成到电路板上&#xff0c;加上外…

googleTest 源码主线框架性分析

本文备忘一个主题的分析过程和结论&#xff0c;即&#xff0c;googleTest框架中是如何调用相关的测试宏的&#xff1f; TEST TEST_F TEST_P 等等 1&#xff0c;googleTest 环境与简单示例 1.1 下载 googletest 并编译 下载&#xff1a; $ git clone https://github.com/goog…

linux环境安装mongoDB

一、安装单体mogodb 目标&#xff1a;在Linux中部署一个单机的MongoDB&#xff0c;作为生产环境下使用。 提示&#xff1a;和Windows下操作差不多。 步骤如下&#xff1a; &#xff08;1&#xff09;先到官网下载压缩包 mongod-linux-x86_64-4.0.10.tgz 。 &#xff08;2&…

细说MCU用定时器控制ADC采样频率的实现方法并通过Simulink查看串口输出波形

目录 一、硬件工程 二、建立Simulink模型 1.安装MATLAB和Simulink 2.建立Simulink模型 三、代码修改 1.修改回调函数 2.产看结果 3.完整的main.c 本文作者的文章 细说MCU用定时器控制ADC采样频率的实现方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/…

《python语言程序设计》第6章4题反向显示一个整数,使用函数reverse 实现 4位数

* def reverse(n):a n // 1000b n % 1000 // 100c n % 1000 % 100 // 10d n % 100 % 10return str(d) str(c) str(b) str(a)numberT 3456 aText reverse(numberT) print(f"{numberT} is reverse number {aText}")

Spring的IoC与DI介绍

Spring基础 Spring Framework系统架构 Spring Framework是Spring生态圈中最基础的项目,是其它项目的根基。 IoC(Inversion ofControl)控制反转 使用对象时,由主动new产生对象转换为由外部提供给对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。Sprin…

AP ERP与汉得SRM系统集成案例(制药行业)

一、项目环境 江西某医药集团公司&#xff0c;是一家以医药产业为主营、资本经营为平台的大型民营企业集团。公司成立迄今&#xff0c;企业经营一直呈现稳健、快速发展的态势&#xff0c; 2008 年排名中国医药百强企业前 20 强&#xff0c;2009年集团总销售额约38亿元人民币…

监控易V7.6.6.15升级详解15:智能预测管理功能

随着企业IT系统的日益复杂和运维需求的不断提高&#xff0c;传统的监控管理方式已经难以满足现代企业的需求。为了应对这一挑战&#xff0c;监控易系统近期对智能预测管理功能进行了全面升级。本次升级不仅优化了原有功能&#xff0c;还新增了一系列实用特性&#xff0c;旨在为…

openstack设置IP直接登录,不需要加dashboard后缀

openstack 实验环境&#xff0c;openstack-t版&#xff0c;centos2009 修改配置文件 [rootcontroller ~]# vim /WEBROOT /etc/openstack-dashboard/local_settings #将dashboard去掉 WEBROOT /dashboard/ #改为 WEBROOT /[rootcontroller ~]# vim /etc/httpd/conf.d/openst…

交叉编译ethtool(ubuntu 2018)

参考文章&#xff1a;https://www.cnblogs.com/nazhen/p/16800427.html https://blog.csdn.net/weixin_43128044/article/details/137953913 1、下载相关安装包 //ethtool依赖libmul git clone http://git.netfilter.org/libmnl //ethtool源码 git clone http://git.kernel.or…

yolov5进行识别安全帽

进行毕业设计 下载yolov5使用按照教程来进行就行注意事项&#xff08;有必要看看&#xff09;效果 总结 下载yolov5 地址是&#xff1a;https://github.com/ultralytics/yolov5 使用按照教程来进行就行 这里简单说一下&#xff1a; 下载需要的命令&#xff1a; pip install -…

腾讯元宝上线“3D角色梦工厂”:快速生成专属3D角色!

7月16日&#xff0c;腾讯旗下大模型应用“腾讯元宝”上线“3D角色梦工厂”&#xff0c;允许用户通过上传一张五官清晰的正面头像&#xff0c;并选择不同的角色模板&#xff0c;迅速生成个人3D角色&#xff01; 技术特点 “3D角色梦工厂”将大模型生成技术与3D应用相结合&#…

【开发实战】QT5 + OpenCV4 开发环境配置应用演示

前言 作为深度学习算法工程师&#xff0c;必须要掌握应用开发技能吗&#xff1f;搞工程肯定是必须要会界面开发&#xff0c;QT就是一个很不错的选择。本文以QT5.15 OpenCV4.8 OpenVINO2023为例&#xff0c;搭建应用开发环境&#xff0c;演示深度学习模型的QT应用案例。 开发…