Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)

本文将详细介绍Java中工厂模式的多种实现方式,包括简单工厂模式、工厂方法模式和抽象工厂模式。我们将通过示例代码和解释来阐述每种工厂模式的特点、使用场景以及优缺点,帮助读者更好地理解和应用这些设计模式。

[参见]:

Java设计模式:核心概述(一)

Java设计模式:单例模式之六种实现方式详解(二)

目录

    • 一、核心概念
      • 工厂模式的优点
      • Java中工厂模式主要三种形态
    • 二、简单工厂模式
      • 优缺点分析
    • 三、工厂方法模式
      • 优缺点分析
    • 四、抽象工厂模式
    • 总结

一、核心概念

在软件设计中,工厂模式是一种常见的设计模式,它提供了一种创建对象的最佳方式。通过工厂模式,我们可以将对象的创建逻辑与使用逻辑分离,降低代码的耦合度,提高系统的可扩展性和可维护性。

Java中的工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

工厂模式的优点

  1. 解耦:将对象的创建和使用过程分开,使得客户端代码无需知道具体对象的创建过程,只需通过工厂获取对象即可。
  2. 降低代码重复:多个地方需要使用对象时,只需调用工厂方法即可,避免了代码的重复。
  3. 减少错误:由于对象的创建过程由工厂统一管理,因此可以减少因客户端错误创建对象而导致的错误。

Java中工厂模式主要三种形态

  1. 简单工厂模式(又称静态工厂方法):定义一个类来负责其他类的实例化,被创建的实例通常具有共同的父类或接口。客户端只需要传入相应的参数,即可获得所需的对象,而无需知道其具体类名。但是,当系统需要引入新的产品时,可能需要修改工厂类的代码,这违反了开闭原则。
  2. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。客户端针对抽象工厂及抽象产品编程,产品的具体工厂和产品由具体工厂子类实现。这样,当系统需要引入新的产品时,只需增加相应的具体工厂和产品类即可,符合开闭原则。
  3. 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。客户端使用一个抽象的工厂接口来创建产品,而具体的工厂类负责生成具体的产品。这样可以将简单工厂模式和工厂方法模式进行整合,使得产品的创建更加灵活和可扩展。

二、简单工厂模式

简单工厂模式是最简单的工厂模式,它通过一个专门的类来负责创建其他类的实例,而不需要明确指定具体将要创建的类。这个专门的类称为“简单工厂类”或“工厂类”。

在Java中,简单工厂模式是一种创建型设计模式,它由一个单独的类(称为工厂类)负责创建其他类的实例,而无需将这些类的实例化逻辑暴露给客户端。

简单工厂模式通常用于创建具有共同父类或接口的对象的场景。

下面是一个简单工厂模式的Java实现示例:

首先,定义一个产品接口或抽象类:

// 产品接口
public interface Product {void use();
}

然后,实现具体产品类:

// 具体产品A
public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用具体产品A");}
}// 具体产品B
public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用具体产品B");}
}

接下来,创建简单工厂类:

// 简单工厂类
public class SimpleFactory {// 静态工厂方法,用于创建产品public static Product createProduct(String type) {if ("A".equals(type)) {return new ConcreteProductA();} else if ("B".equals(type)) {return new ConcreteProductB();} else {throw new IllegalArgumentException("不支持的产品类型: " + type);}}
}

最后,客户端代码可以这样使用简单工厂:

public class Client {public static void main(String[] args) {// 通过简单工厂创建产品AProduct productA = SimpleFactory.createProduct("A");productA.use(); // 输出:使用具体产品A// 通过简单工厂创建产品BProduct productB = SimpleFactory.createProduct("B");productB.use(); // 输出:使用具体产品B// 尝试创建不存在的产品类型将抛出异常// Product productC = SimpleFactory.createProduct("C");}
}

SimpleFactory类包含一个静态方法createProduct,它根据传入的类型字符串创建并返回相应的产品实例。客户端代码通过调用这个静态工厂方法来获取产品实例,而无需直接与具体的产品类交互。

优缺点分析

  • 简单工厂模式的优点在于它将对象的创建逻辑集中在一个单独的工厂类中,从而减少了客户端与具体类之间的耦合;
  • 同时简单易懂,易于实现。
  • 然而,它的一个缺点是当需要引入新的产品类型时,可能需要修改工厂类的代码,这违反了开闭原则(即对扩展开放,对修改封闭)
  • 此外,简单工厂模式还可能导致系统结构变得复杂和难以维护。

在实际应用中,如果需要频繁添加新的产品类型,可能会更倾向于使用抽象工厂模式或其他更灵活的工厂模式。

三、工厂方法模式

工厂方法模式是对简单工厂模式的进一步抽象化。它将对象的创建延迟到子类中进行,从而实现了更好的扩展性。在工厂方法模式中,抽象工厂类负责定义创建产品对象的接口,而具体工厂类则负责实现具体的产品创建逻辑。

下面是一个工厂方法模式的Java实现示例:

首先,定义一个产品接口:

// 产品接口
public interface Product {void use();
}

然后,实现具体产品类:

// 具体产品A
public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用具体产品A");}
}// 具体产品B
public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用具体产品B");}
}

接下来,创建抽象工厂类,并定义工厂方法:

// 抽象工厂类
public abstract class Creator {// 工厂方法,声明为抽象方法,由子类实现public abstract Product factoryMethod();// 一个通用的方法,使用工厂方法来创建产品public final Product createProduct() {Product product = factoryMethod();// 这里可以添加一些额外的逻辑,比如初始化产品等return product;}
}

然后,实现具体的工厂子类:

// 具体工厂子类A,用于创建ConcreteProductA
public class ConcreteCreatorA extends Creator {@Overridepublic Product factoryMethod() {return new ConcreteProductA();}
}// 具体工厂子类B,用于创建ConcreteProductB
public class ConcreteCreatorB extends Creator {@Overridepublic Product factoryMethod() {return new ConcreteProductB();}
}

最后,客户端代码可以这样使用工厂方法模式:

public class Client {public static void main(String[] args) {// 使用具体工厂子类A来创建产品ACreator creatorA = new ConcreteCreatorA();Product productA = creatorA.createProduct();productA.use(); // 输出:使用具体产品A// 使用具体工厂子类B来创建产品BCreator creatorB = new ConcreteCreatorB();Product productB = creatorB.createProduct();productB.use(); // 输出:使用具体产品B}
}

例子中,Creator是一个抽象工厂类,它声明了一个抽象方法factoryMethod(),这个方法由具体的工厂子类(ConcreteCreatorAConcreteCreatorB)来实现。客户端代码通过创建具体工厂类的实例并调用createProduct()方法来创建产品。每个具体工厂类都知道如何创建和返回一种具体的产品。

优缺点分析

  • 工厂方法模式的优点在于它允许系统在不修改具体工厂类的情况下引进新的产品,这符合开闭原则。
  • 此外,它还提供了一种将产品类与客户端代码解耦的机制,因为客户端代码只依赖于抽象产品和抽象工厂,而不是具体实现。
  • 同时,它也降低了代码的耦合度,提高了系统的可扩展性和可维护性。
  • 但是,与简单工厂模式相比,工厂方法模式需要定义更多的类,增加了系统的复杂度。

四、抽象工厂模式

抽象工厂模式是对工厂方法模式的进一步抽象化。它提供了一组用于创建一系列相关或互相依赖对象的接口,而不需要指定它们具体的类。在抽象工厂模式中,抽象工厂类负责定义创建产品家族的接口,而具体工厂类则负责实现具体的产品家族创建逻辑。产品家族是指位于不同产品等级结构中,功能相关联的产品组成的家族。

以下是Java中实现抽象工厂模式的一种方式:

首先,定义产品接口:

// 产品A的接口
public interface ProductA {void use();
}// 产品B的接口
public interface ProductB {void use();
}

然后,实现具体产品:

// 具体产品A1
public class ConcreteProductA1 implements ProductA {@Overridepublic void use() {System.out.println("使用具体产品A1");}
}// 具体产品A2
public class ConcreteProductA2 implements ProductA {@Overridepublic void use() {System.out.println("使用具体产品A2");}
}// 具体产品B1
public class ConcreteProductB1 implements ProductB {@Overridepublic void use() {System.out.println("使用具体产品B1");}
}// 具体产品B2
public class ConcreteProductB2 implements ProductB {@Overridepublic void use() {System.out.println("使用具体产品B2");}
}

接下来,定义抽象工厂接口:

public interface AbstractFactory {ProductA createProductA();ProductB createProductB();
}

然后,实现具体工厂:

// 具体工厂1,负责创建产品A1和产品B1
public class ConcreteFactory1 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA1();}@Overridepublic ProductB createProductB() {return new ConcreteProductB1();}
}// 具体工厂2,负责创建产品A2和产品B2
public class ConcreteFactory2 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA2();}@Overridepublic ProductB createProductB() {return new ConcreteProductB2();}
}

最后,客户端代码可以这样使用抽象工厂:

public class Client {public static void main(String[] args) {// 使用具体工厂1来创建产品AbstractFactory factory1 = new ConcreteFactory1();ProductA productA1 = factory1.createProductA();ProductB productB1 = factory1.createProductB();productA1.use(); // 输出:使用具体产品A1productB1.use(); // 输出:使用具体产品B1// 使用具体工厂2来创建产品AbstractFactory factory2 = new ConcreteFactory2();ProductA productA2 = factory2.createProductA();ProductB productB2 = factory2.createProductB();productA2.use(); // 输出:使用具体产品A2productB2.use(); // 输出:使用具体产品B2}
}

例子中,AbstractFactory接口定义了两个创建产品的方法,createProductA()createProductB()ConcreteFactory1ConcreteFactory2分别实现了这些方法以创建不同的具体产品。客户端代码通过具体的工厂来创建和使用产品,而不需要直接与具体产品类打交道。这种方式提供了更好的灵活性和可扩展性,因为你可以在不修改客户端代码的情况下增加新的工厂和产品。

总结

在实际开发中,抽象工厂模式的实现可能更加复杂和庞大。为了保持代码的清晰和可维护性,我们需要合理地划分产品等级结构和产品家族,并遵循单一职责原则和开闭原则进行设计。同时,在实际应用中,我们可以根据具体需求选择使用简单工厂模式、工厂方法模式或抽象工厂模式中的任意一种或多种组合使用以满足需求。例如,在某些情况下,我们可以将简单工厂模式与工厂方法模式结合使用以简化代码结构并提高灵活性;而在其他情况下,我们可能需要使用抽象工厂模式来处理更复杂的对象创建场景。

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

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

相关文章

sqoop-import 详解

文章目录 前言一、介绍1. sqoop简介2. sqoop import的作用3. 语法3.1 sqoop import 语法3.2 导入配置属性 二、导入参数1. 常见参数2. 验证参数3. 导入控制参数4. 用于覆盖映射的参数5. 增量导入参数6. 输出行格式参数7. 输入解析参数8. Hive 参数9. HBase 参数10. Accumulo 参…

栈和队列之队列

1.队列 1.1队列的概念 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队…

数学建模【基于熵权法对TOPSIS模型的修正】

一、修正原因 在前面有关于TOPSIS法和熵权法的介绍,我们可以知道TOPSIS有一个和层次分析法共有的问题,就是各个指标的权重是由我们主观或根据专家的主观评价估计的。层次分析法这个问题还更甚之。而恰好熵权法中的权重是由数据确定的,绝对客…

009-CSS-Less、Scss、Sass

Less、Scss、Sass 概念常用的扩展能力1、嵌套:代码层级清晰2、变量:项目风格抽离3、混合:公共样式封装4、&:当前选择器的父级5、导入:公共文件抽离 在实际工作中,CSS 编写更多的采用 CSS 扩展语言&…

Git 撤销修改

如果我们在我们的工作区写了很长时间代码,发现出现错误,想回退到之前的版本,这时改怎么做呢? 情况一:对于工作区的代码,还没有 add 我们当然也可以使用git diff 查看与上次提交的差异,进行手动删…

【场景题】让你设计一个订单号生成服务,该怎么做?

方案 当设计订单号生成服务时,我们需要考虑唯一性、数据量、可读性、基因法、可扩展性、高性能和高可用性等多个方面。根据这些考虑,一个简单的订单号生成服务设计方案可以采取以下措施: 使用Snowflake算法或第三方分布式ID生成器&#xff…

【力扣经典面试题】58. 最后一个单词的长度

目录 一、问题描述 二、解题思路 三、代码实现(C版) 四、总结 喜欢可以点赞关注哦!谢谢大家哦 一、问题描述 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 …

PackagesNotFoundError:学习利用报错信息找到解决方法

反思:之前看到报错经常是直接复制报错信息去网上搜,但很多情况下报错信息里其实就给出了解决方案 报错信息: Collecting package metadata (current_repodata.json): done Solving environment: unsuccessful initial attempt using frozen …

FRM模型十五:净值归因之Fama_French三因子模型

文章目录 一、起源二、构建因子三、投资组合的净值归因1. 市场因子2. 规模因子3.价值因子4. 基于净值的归因方法 三、代码实现 一、起源 在多因子模型推出之前,CAPM模型被视为资产定价的第一标准。随着市场不断发展,发现了越来越多CAPM模型无法解释的现…

java中的this

在Java中,this 是一个关键字,用于引用当前对象。它可以用于访问当前对象的实例变量和方法。this 关键字通常在以下情况下使用:区分实例变量和局部变量:当实例变量和局部变量名称相同时,可以使用 this 关键字来引用实例…

【linux】crontab定时任务介绍

1.简介2.crontab语法3.crontab限制4.crontab配置文件5.crontab格式介绍 1.简介 Linux crontab 是用来定期执行程序的命令。 当安装完成操作系统之后,默认便会启动此任务调度命令。 crond 命令每分钟会定期检查是否有要执行的工作,如果有要执行的工作便…

u-boot的DM驱动模型

0、本文基于U-Boot 2022.01-v2.07版本进行分析。 1、u-boot编译流程简要分析 2、u-boot启动流程简要分析 3、u-boot增加自定义命令 4、u-boot的DM驱动模型 4.1、参考资料 Uboot中的DM驱动模型:这篇文章详细介绍了DM驱动模型的原理。 本文重点整理了几个数据结构…

超详细的Scrapy框架的基本使用教程

Scrapy的介绍 scrapy的工作流程(重点!!!) 如下图所示: 爬虫: 负责向引擎提供要爬取网页的URL,引擎会把这个URL封装成request对象并传递给调度器,把引擎传递过来的resp…

常用区块链安全关键名词和攻击方法解释

文章目录 一、重点名词解释1.1 钱包(Wallet)1.2 冷钱包(Cold Wallet)1.3 热钱包 (Hot Wallet)1.4 公钥(Public Key)1.5 私钥(Private Key)1.6 助记词(Mnemonic)1.7 Keystore1.8 公链(Public Blockchain)1.9 交易所 Exchange1.10 节…

API 常用的序列化协议详解

目录 JSON (JavaScript Object Notation) XML (Extensible Markup Language) Protocol Buffers (Protobuf) MessagePack Thrift Avro 小结 API(Application Programming Interface,应用程序编程接口)在软件开发中扮演着至关重要的角色…

leetcode 673.最长递增子序列的个数

上一题只需要知道最长递增子序列的长度就行了,那样的话直接一个dp就完事了,但是呢,这里说了需要记录这个最长长度递增子序列的个数,这下的话,如果你想用原先的思路,其实可以,但是要能做到计数的…

vue3 (六)自定义指令

1.定义自定义指令: app.directive(pos,{mounted(el,bunding){el.style[bunding.arg] bunding.value px;}, updated(el,bunding){el.style[bunding.arg] bunding.value px;} }) app.directive(指令名,{ mounted(el,bunding){}, updated(el,bunding){} }) 如果只…

安卓app软件开发的费用

我们公司总结的开发价格根据安卓APP,苹果APP行业的报价,开发的APP软件费用主要受到两个方面的影响。安卓和苹果APP软件应用的复杂性,第二个是定制开发的APP软件,开发成本和人员的投入成本,以下就是不同的报价费用是怎么…

面试经典150题【51-60】

文章目录 面试经典150题【51-60】71.简化路径155.最小栈150.逆波兰表达式求值224.基本计算器141.环形链表2.两数相加21.合并两个有序链表138.随机链表的复制19.删除链表的倒数第N个节点82.删除链表中的重复元素II 面试经典150题【51-60】 71.简化路径 先用split(“/”)分开。然…

10. Nginx进阶-Return

简介 什么是Return? nginx的return指令是用于在nginx配置文件中进行重定向或返回特定的HTTP响应码的指令。 它可以根据不同的条件来执行不同的操作,如重定向到其他URL、返回指定的HTTP响应码或自定义响应内容等。 Return适用范围 return指令只能在se…