Java 设计模式——工厂方法模式

news2025/1/14 18:20:47

目录

  • 1.案例分析
  • 2.简单工厂模式
    • 2.1.结构
    • 2.2.案例实现
      • 2.2.1.抽象产品
      • 2.2.2.具体产品
      • 2.2.3.具体工厂
      • 2.2.4.测试
    • 2.3.优缺点
    • 2.4.扩展
  • 3.✨工厂方法模式
    • 3.1.介绍
    • 3.1.结构
    • 3.2.案例实现
      • 3.2.1.抽象工厂
      • 3.2.2.具体工厂
      • 3.2.3.测试
    • 3.3.优缺点
    • 3.4.应用场景

1.案例分析

【需求】设计一个咖啡店点餐系统。设计一个咖啡类 (Coffee),并定义其两个子类:美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】;再设计一个咖啡店类 (CoffeeStore),咖啡店具有点咖啡的功能。具体类的设计如下:
在这里插入图片描述
在 Java 中,万物皆对象,这些对象都需要创建,如果创建的时候直接 new 该对象,就会对该对象耦合严重,假如要更换对象,所有 new 对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说工厂模式最大的优点就是:解耦。

2.简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

2.1.结构

简单工厂包含如下角色:

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现或者继承抽象产品的子类。
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品。

2.2.案例实现

现在使用简单工厂对上面案例进行改进,类图如下:
在这里插入图片描述

2.2.1.抽象产品

Coffee.java

public abstract class Coffee {
    
    public abstract String getName();
    
    //加糖
    public void addSugar(){
        System.out.println("加糖");
    }
    
    //加奶
    public void addMilk(){
        System.out.println("加奶");
    }
}

2.2.2.具体产品

AmericanCoffee.java

//美式咖啡
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

LatteCoffee.java

//拿铁咖啡
public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

2.2.3.具体工厂

SimpleCoffeeFactory.java

public class SimpleCoffeeFactory {
    
    public Coffee createCoffee(String type) {
        //声明 Coffee 类型的变量,根据不同类型创建不同的 coffee 子类对象
        Coffee coffee = null;
        if("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("对不起,您所点的咖啡没有");
        }
        return coffee;
    }
}

CoffeeStore.java

public class CoffeeStore {
    public Coffee orderCoffee(String type){
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        //调用生产咖啡的方法
        Coffee coffee = factory.createCoffee(type);
    
        //加配料
        coffee.addMilk();
        coffee.addSugar();
    
        return coffee;
    }
}

2.2.4.测试

Clien.java

public class Clien {
    public static void main(String[] args) {
        // 1.创建咖啡店类
        CoffeeStore store = new CoffeeStore();
        // 2.点咖啡
        Coffee coffee = store.orderCoffee("american");
        System.out.println(coffee.getName());
    }
}

工厂 (factory) 处理创建对象的细节,一旦有了 SimpleCoffeeFactory,CoffeeStore 类中的 orderCoffee() 就变成此对象的客户,后期如果需要 Coffee 对象直接从工厂中获取即可。这样也就解除了和 Coffee 实现类的耦合,但同时又产生了新的耦合,CoffeeStore 对象和SimpleCoffeeFactory 工厂对象的耦合,工厂对象和商品对象的耦合。后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory 的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

2.3.优缺点

  • 优点:封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
  • 缺点:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

2.4.扩展

静态工厂:在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它不是 23 种设计模式中的。代码如下:

public class SimpleCoffeeFactory {
	public static Coffee createCoffee(String type) {
		Coffee coffee = null;
		if("americano".equals(type)) {
			coffee = new AmericanoCoffee();
		} else if("latte".equals(type)) {
			coffee = new LatteCoffee();
		}
		return coffe;
	}
}

3.✨工厂方法模式

3.1.介绍

工厂方法模式 (Factory Method Pattern) 是一种创建型设计模式,它定义了一个用于创建对象的接口,但将具体的对象创建推迟到子类中进行。在工厂方法模式中,我们通过定义一个抽象工厂类(通常是一个接口),其中包含一个创建对象的抽象方法。具体的对象创建则由实现了抽象工厂类的具体工厂类来完成,每个具体工厂类负责实例化一种具体的对象。这样就将对象的创建和使用分离开来,便于系统的扩展和解耦。

3.1.结构

工厂方法模式的主要角色:

  • 抽象工厂 (Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂 (Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品 (Abstract Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 (Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

3.2.案例实现

使用工厂方法模式对上例进行改进,类图如下:
在这里插入图片描述

3.2.1.抽象工厂

CoffeeFactory.java

//抽象工厂,提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
public interface CoffeeFactory {
    //生产咖啡
    Coffee createCoffee();
}

3.2.2.具体工厂

AmericanCoffeeFactory.java

//具体工厂:生产美式咖啡
public class AmericanCoffeeFactory implements CoffeeFactory{
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

LatteCoffeeFactory.java

//具体工厂:生产拿铁咖啡
public class LatteCoffeeFactory implements CoffeeFactory{
    
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

CoffeeStore.java

//咖啡店
public class CoffeeStore {
    
    //创建咖啡工厂
    private CoffeeFactory factory;
    
    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }
    
    //点咖啡功能
    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        //加配料
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}

3.2.3.测试

Client.java

public class Client {
    public static void main(String[] args) {
        //创建咖啡店对象
        CoffeeStore coffeeStore = new CoffeeStore();
        //创建具体的咖啡店工厂
        CoffeeFactory factory = new AmericanCoffeeFactory();
        //CoffeeFactory factory = new LatteCoffeeFactory();
        coffeeStore.setFactory(factory);
        //点咖啡
        Coffee coffee = coffeeStore.orderCoffee();
        System.out.println(coffee.getName());
    }
}

从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且也克服了其缺点。

3.3.优缺点

(1)优点

  • 将对象的创建和使用相分离:工厂方法模式将对象的创建逻辑封装在具体工厂类中,客户端只需通过工厂接口或抽象类来使用产品,无需关心具体的创建过程,从而实现了对象的创建和使用的解耦。
  • 可以轻松添加新的产品:由于每个具体工厂类负责实例化一种具体的产品对象,如果需要添加新的产品,只需要添加一个新的具体工厂类即可,不需要修改现有代码,符合开闭原则。
  • 灵活性好:工厂方法模式可以根据具体需求选择不同的具体工厂类来创建对象,从而实现不同的行为和逻辑。客户端只需要知道工厂接口或抽象类,无需关心具体的实现细节。

(2)缺点

  • 类的个数增加:由于每个具体产品类都需要对应一个具体工厂类,因此如果产品种类较多,会导致具体工厂类的数量增加,增加了系统的复杂性。
  • 增加了系统的抽象性和理解难度:相比于简单工厂模式,工厂方法模式引入了抽象工厂类和具体工厂类之间的层级关系,增加了系统的抽象性和理解难度,需要对抽象工厂、具体工厂及其之间的关系有深入的理解。

3.4.应用场景

(1)工厂方法模式适用于以下应用场景:

  • 对象的创建需要根据不同的条件或参数进行灵活配置:当对象的创建过程比较复杂,需要根据不同的条件或参数来配置对象的创建逻辑时,可以使用工厂方法模式。每个具体工厂类可以根据不同的条件或参数来创建对应的产品对象。
  • 需要扩展或添加新的产品时:当系统需要添加新的产品时,可以通过扩展或添加新的具体工厂类来实现,而无需修改现有的客户端代码。适用于符合开闭原则的情况。
  • 隐藏对象创建的细节:当需要将对象的创建细节隐藏起来,只对外提供一个抽象的工厂接口或抽象类时,可以使用工厂方法模式。客户端只需关心抽象工厂和产品接口,无需关心具体的产品创建过程。
  • 通过工厂方法进行依赖注入:在依赖注入 (Dependency Injection) 的场景中,可以使用工厂方法模式来创建和注入依赖对象。具体工厂类可以负责创建和管理依赖对象的生命周期,并将创建好的对象注入到需要它的类中。

(2)总而言之,工厂方法模式适用于需要根据不同条件创建不同对象、需要动态添加新产品、隐藏对象创建细节或进行依赖注入的场景。

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

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

相关文章

python获取职教云信息

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章 ⭐作者主页:逐梦苍穹 ⭐所属专栏:项目。 目录 1、需求1.1、推荐课程1.2、课程详情 2、思路分析2.1、推荐课程完整代码 2.2、课程详情找到JSON数据…

4.2.tensorRT基础(1)-第一个trt程序,实现模型编译的过程

目录 前言1. hello案例2. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 tensorRT 基础-第一个 trt 程序&#x…

SlickGrid学习

options: 选项 设置 enableCellNavigation 启用单元格导航,可以点单元格 enableColumnReorder 启动拖拽列 example-colspan.html 跨列实例 AutoTooltips plugin 隐藏列文字时自动显现列标题全文 Checkbox row select column 增加选择列来选择行…

STM32入门学习之core_cm3问题

1.安装了keil之后,新建工程出现几百个关于core_cm3的问题,百思不得其解。后在网上查阅资料后,了解到可能是keil版本的问题,是因为我下载的keill版本太高了,内部不支持ARM5.06的编译器。出现很多关于core_cm3的问题是因…

使用java语言制作一个窗体(弹窗),用来收集用户输入的内容

前言 最近做的一个需求,有个逻辑环节是:需要从本地保存的xml文件中取出一个值,这个值是会变化的。然后项目经理就给我说,你能不能做一个小工具,让用户可以直接通过界面化操作将这个变化的值写入文件,不用再…

rv1126交叉编译

目录 一、解压sdk二、交叉编译出动态库sqlite3交叉编译opencv交叉编译一、解压sdk tar xzvf rv1126_rv1109_linux_sdk_v1.8.0_PureVersion.tar.gz 查看交叉编译工具链 pwd查看绝对路径/home

正斜杠“/” 和反斜杠 “\” 傻傻分不清?

Note, on your keyboard, the location of two different punctuation marks—/, or forward slash, and \, or backward slash (also known as backslash). As you read from left to right, the forward slash falls forward while the backslash falls backward; 引用自 《…

js逆向思路-区分各个瑞数版本vmp/3/4/5/6代

目录 一、如何区分是最新瑞数vmp反爬二、3/4/5/6代/vmp版本的瑞数网站特征举例三、瑞数反爬的解决思路四、推荐相关瑞数文章五、一些心得一、如何区分是最新瑞数vmp反爬 前言:本篇文章不会介绍详细的解决反爬的算法扣代码过程,只是一些经验闲谈,文章的末尾有相关的好的质量的…

油猴脚本-Bilibili剧场模式仿Youtube

对比某个不存在的视频网站(YouTube),以及B站的播放模式,普通模式以及网页全屏之间都有一个“中间档”,油管的叫 剧场模式,B站的叫 宽屏模式。 剧场模式 宽屏模式 相比之下,还是更喜欢油管的剧…

10.带你入门matlab频率表、盒图(matlab程序)

1.简述 相关概念介绍 以信号为例,信号在时域下的图形可以显示信号如何随着时间变化,而信号在频域下的图形(一般称为频谱)可以显示信号分布在哪些频率及其比例。频域的表示法除了有各个频率下的大小外,也会有各个频率的…

《遗留系统现代化》读书笔记(基础篇)

你现在所写的每一行代码,都是未来的遗留系统 为什么要对遗留系统进行现代化? 什么是遗留系统? 判断遗留系统的几个维度:代码、架构、测试、DevOps 以及技术和工具。时间长短并不是衡量遗留系统的标准。代码质量差、架构混乱、没…

【C++】多线程编程系列总纲

本系列文章主要介绍C11并发编程,主要包括如下篇目: 1、多线程编程一(初识并发和多线程) https://blog.csdn.net/Jacky_Feng/article/details/131751373?csdn_share_tail{"type"%3A"blog"%2C"rType&qu…

idea快捷方式,(主要是自己用的,持续收集)

编辑器 代码光标扩大选中 快捷键名字 使用方式 此时光标在这这里,按一下发现选中面积扩大, 如果再按快捷键,选中面积还扩大

带你【玩转Linux命令】➾ find cut 每天2个day06

带你【玩转Linux命令】➾ find & cut 每天2个day06 🔻 一、文件管理命令1.1 find-查找文件或目录1.2 cut-指定欲显示的文件内容,输出到标准输出设备 🔻 总结—温故知新 🔻 一、文件管理命令 1.1 find-查找文件或目录 &#x…

【Nacos】基于k8s容器化部署Nacos集群

近期,在机器上部署了三个节点的nacos集群服务用于几个小型微服务的注册配置中心,并使用了Nginx简单代理了一下,随即简单研究了下集群部署分布式部署稍微提高可用性。部署完后能够正常使用,但是发现一个问题,刷新Nacos集…

制作文件间链接

制作文件间链接 管理文件间链接 硬/软链接 创建指向同一个文件的多个名称。 创建硬链接 从初始名称到文件系统的数据,每个文件都以一个硬链接开始。当创建指向文件的新硬链接时,也会创建另一个指向同一数据的名称。新硬链接与原始文件名作用相同。一经创建&…

Maya适合哪个工作站?

Autodesk Maya 提供多种功能,可以适应电影、游戏和建筑等不同行业的需求。定制的 Autodesk Maya 工作站可以帮助您提高行业领先的 3D 计算机动画、建模、模拟和渲染软件的工作效率和用户体验。 根据您的特定需求定制的快速、强大的工作站可以帮助您充分利用 Maya 工…

高薪Offer收割机之Redis分布式锁

锁在应用开发中使用非常广泛,哪些场景需要使用锁呢? 我们先来看抢购优惠卷的场景,代码如下: public void rushToPurchase() throws InterruptedException {//获取优惠券数量Integer num = (Integer) redisTemplate.opsForValue().get(“num”);//判断是否抢完if (null == n…

[Ipsc2009]Let there be rainbows!

Description HY Star是一个处处充满和谐,人民安居乐业的星球,但是HY Star却没有被评上宇宙文明星球,很大程度上是因为 星球的形象问题。HY Star由N个国家组成,并且在一些国家之间修建了道路以方便交流。由于HY Star是一个和谐的 星球&#x…

【运维】第04课:入口网关服务注册发现-Openrety 动态 uptream

本课时,我将带你一起了解入口网关服务的注册发现,并使用 OpenResty 实现一套动态 Upstream。 课前学习提示 基于本课时我们将要学习的内容,我建议你课前先了解一下 Nginx 的基础,同时熟悉基础的 Lua 语言语法,另外再回顾一下 HTTP 的请求过程,对于 Nginx 的负载均衡基本…