设计模式之工厂方法模式解析

工厂方法模式
1)问题

简单工厂模式

当需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,需要修改工厂类的源代码。

2)概述

针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

在这里插入图片描述

3)角色
Product(抽象产品):定义产品的接口,是工厂方法模式所创建对象的超类型,是产品对象的公共父类。ConcreteProduct(具体产品):实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。ConcreteFactory(具体工厂):是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

代码示例

interface Factory {public Product factoryMethod();
}class ConcreteFactory implements Factory {public Product factoryMethod() {return new ConcreteProduct();}
}

注意

在实际使用时,具体工厂类在实现工厂方法时除了创建具体产品对象之外,还可以负责产品对象的初始化及一些资源和环境的配置工作,例如连接数据库、创建文件等。

可以通过配置文件来存储具体工厂类 ConcreteFactory 的类名,更换新的具体工厂时无须修改源代码,系统扩展更为方便。

4)案例-初始方案

问题

工厂类包含大量的if…else…代码,导致维护和测试难度增大;

系统扩展不灵活,增加新类型的日志记录器,必须修改静态工厂方法的业务逻辑。

在这里插入图片描述

5)案例-重构后

Logger 接口充当抽象产品,其子类 FileLogger 和 DatabaseLogger 充当具体产品,LoggerFactory 接口充当抽象工厂,其子类FileLoggerFactory 和 DatabaseLoggerFactory 充当具体工厂。

在这里插入图片描述

//日志记录器接口:抽象产品
interface Logger {public void writeLog();
}//数据库日志记录器:具体产品
class DatabaseLogger implements Logger {public void writeLog() {System.out.println("数据库日志记录。");}
}//文件日志记录器:具体产品
class FileLogger implements Logger {public void writeLog() {System.out.println("文件日志记录。");}
}//日志记录器工厂接口:抽象工厂
interface LoggerFactory {public Logger createLogger();
}//数据库日志记录器工厂类:具体工厂
class DatabaseLoggerFactory implements LoggerFactory {public Logger createLogger() {//连接数据库,代码省略//创建数据库日志记录器对象Logger logger = new DatabaseLogger(); //初始化数据库日志记录器,代码省略return logger;}	
}//文件日志记录器工厂类:具体工厂
class FileLoggerFactory implements LoggerFactory {public Logger createLogger() {//创建文件日志记录器对象Logger logger = new FileLogger(); //创建文件,代码省略return logger;}	
}class Client {public static void main(String args[]) {LoggerFactory factory;Logger logger;factory = new FileLoggerFactory(); //可引入配置文件实现logger = factory.createLogger();logger.writeLog();}
}
6)案例-使用反射和配置文件优化

在客户端代码中不再使用 new 关键字创建工厂对象,而是将具体工厂类的类名存储在配置文件中,通过读取配置文件获取类名字符串,再使用 Java 的反射机制,根据类名字符串生成对象。

//通过类名生成实例对象并将其返回
Class c=Class.forName("String");
Object obj=c.newInstance();
return obj;

配置文件 config.xml 存储具体工厂类类名

<!— config.xml -->
<?xml version="1.0"?>
<config><className>FileLoggerFactory</className>
</config>

XML工具类

//工具类XMLUtil.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象public static Object getBean() {try {//创建DOM文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;							doc = builder.parse(new File("config.xml")); //获取包含类名的文本节点NodeList nl = doc.getElementsByTagName("className");Node classNode=nl.item(0).getFirstChild();String cName=classNode.getNodeValue();//通过类名生成实例对象并将其返回Class c=Class.forName(cName);Object obj=c.newInstance();return obj;}   catch(Exception e) {e.printStackTrace();return null;}}
}

客户端代码

class Client {public static void main(String args[]) {LoggerFactory factory;Logger logger;factory = (LoggerFactory)XMLUtil.getBean(); //getBean()的返回类型为Object,需要进行强制类型转换logger = factory.createLogger();logger.writeLog();}
}

引入XMLUtil类和XML配置文件后,增加新的日志记录方式,只需如下步骤

新的日志记录器需要继承抽象日志记录器Logger;

对应增加一个新的具体日志记录器工厂,继承抽象日志记录器工厂LoggerFactory,并实现其中的工厂方法createLogger(),返回具体日志记录器对象;

修改配置文件config.xml,将新增的日志记录器工厂类的类名替换原有工厂类类名;

编译新增的日志记录器类和日志记录器工厂类,原有类库代码无须做任何修改。

7)案例-使用方法重载和隐藏进行优化
1.方法重载

在这里插入图片描述

引入重载方法后,抽象工厂 LoggerFactory 的代码如下:

interface LoggerFactory {public Logger createLogger();public Logger createLogger(String args);public Logger createLogger(Object obj);
}

具体工厂类 DatabaseLoggerFactory 的代码如下:

class DatabaseLoggerFactory implements LoggerFactory {public Logger createLogger() {//使用默认方式连接数据库,代码省略Logger logger = new DatabaseLogger(); //初始化数据库日志记录器,代码省略return logger;}public Logger createLogger(String args) {//使用参数args作为连接字符串来连接数据库,代码省略Logger logger = new DatabaseLogger(); //初始化数据库日志记录器,代码省略return logger;}	public Logger createLogger(Object obj) {//使用封装在参数obj中的连接字符串来连接数据库,代码省略Logger logger = new DatabaseLogger(); //使用封装在参数obj中的数据来初始化数据库日志记录器,代码省略return logger;}	
}
2.方法隐藏

为进一步简化客户端的使用,还可以对客户端隐藏工厂方法;

在工厂类中调用产品类的业务方法,客户端无须调用工厂方法创建产品,直接通过工厂即可使用所创建的对象中的业务方法。

在这里插入图片描述

抽象工厂类 LoggerFactory 的代码如下:

//改为抽象类
abstract class LoggerFactory {//在工厂类中直接调用日志记录器类的业务方法writeLog()public void writeLog() {Logger logger = this.createLogger();logger.writeLog();}public abstract Logger createLogger();	
}

客户端代码修改如下:

class Client {public static void main(String args[]) {LoggerFactory factory;factory = (LoggerFactory)XMLUtil.getBean();factory.writeLog(); //直接使用工厂对象来调用产品对象的业务方法}
}
8)总结
1.优点

只需要关心所需产品对应的工厂,无须关心创建细节,无须知道具体产品类的类名。

在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,只要添加一个具体工厂和具体产品就可以。

2.缺点

添加新产品时,需要编写新的具体产品类,还要提供与之对应的具体工厂类,系统中类的个数将成对增加,会给系统带来额外开销。

考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层定义,增加了系统的抽象性和理解难度,且在实现时需要用到DOM、反射等技术,增加了系统的实现难度。

3.适用场景

客户端不需要知道具体产品类的类名,只需要知道对应的工厂即可。

抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

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

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

相关文章

我的保研材料全部损坏了!这个压缩包文件格式未知或数据已经被损坏不可预料的压缩文件末端

求助各位友友&#xff0c;我的保研材料全部没了&#xff01; 之前为了清理D盘&#xff0c;把之前保研期间准备的几个G的材料全部压缩放在了U盘&#xff0c;但是现在却损坏打不开了&#xff0c;之前为了省事也没有添加过“恢复记录”&#xff01;&#xff01;&#xff01; 先声…

阿赵UE学习笔记——20、角色蓝图和动画蓝图

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次来看看角色控制动画相关的东西&#xff0c;主要用到了动画蓝图和角色蓝图。 一、动画蓝图 之前分析过&#xff0c;蓝图对于虚幻引擎来说&#xff0c;是存在于各个系统里面的&#xff0c;相当…

智慧公厕:卫生、便捷、安全的新时代厕所变革

在城市快速发展的背景下&#xff0c;公共厕所的建设和管理变得越来越重要。智慧公厕作为厕所变革的一项全新举措&#xff0c;通过建立公共厕所全面感知监测系统&#xff0c;以物联网、互联网、大数据、云计算、自动化控制技术为支撑&#xff0c;实现对公共厕所的智能化管理和运…

FPGA学习_时序约束以及VIVADO时序报告

文章目录 前言时序约束的目的一、时序约束种类1、约束主时钟2、约束衍生时钟3、约束虚拟时钟4、input delay5、output delay6、约束异步时钟组7、约束互斥时钟8、假路径约束9、多周期约束 二、VIVADO时序报告三、从时序的角度看为什么寄存器赋值慢一拍 前言 一边学习一边补充当…

容器中的大模型(三)| 利用大语言模型:容器化高效地部署 PDF 解析器实践...

作者&#xff1a;宋文欣&#xff0c;智领云科技联合创始人兼CTO 01 简介 大语言模型&#xff08;LLMs&#xff09;正逐渐成为人工智能领域的一颗璀璨明星&#xff0c;它们的强大之处在于能够理解和生成自然语言&#xff0c;为各种应用提供了无限可能。为了让这些模型更好地服务…

【Hadoop】Hadoop 编译源码

目录 为什么要源码编译Hadoop 编译源码1前期工作准备2jar 包安装2.1安装 Maven2.2安装 ant2.3安装 glibc-headers 和 g2.4安装 make 和 cmake2.5安装 protobuf2.6安装 openssl 库2.7安装 ncurses-devel 库 3编译源码3.1解压源码到 /opt/ 目录3.2 进入到 hadoop 源码主目录 /opt…

AI时代Python金融大数据分析实战:ChatGPT让金融大数据分析插上翅膀

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

UniTask 异步任务

文章目录 前言一、UniTask是什么&#xff1f;二、使用步骤三、常用的UniTask API和示例1.编写异步方法2.处理异常3.延迟执行4.等待多个UniTask或者一个UniTas完成5.异步加载资源示例6.手动控制UniTask的完成状态7.UniTask.Lazy延迟任务的创建8.后台线程切换Unity主线程9.不要返…

第二证券|热度飙升,出境游人数有望破亿,这些概念股被机构盯上

在免签和航班批量康复的方针利好下&#xff0c;本年出境游商场迎来炽热升温。 清明出境游有望爆火 3月20日&#xff0c;Airbnb爱彼迎在北京举行春季出境游趋势发布会举行。 爱彼迎中国数据显示&#xff0c;本年清明节期间的出境游查找热度已经超出2023年同期的2.5倍&#xf…

计算机网络面经-什么是IPv4和IPv6?

前言 Internet协议&#xff08;IP&#xff09;是为连接到Internet网络的每个设备分配的数字地址。它类似于电话号码&#xff0c;是一种独特的数字组合&#xff0c;允许用户与他人通信。IP地址主要有两个主要功能。首先&#xff0c;有了IP&#xff0c;用户能够在Internet上被识别…

Redis 安装(二)

Redis安装说明 大多数企业都是基于Linux服务器部署项目&#xff0c;而且Redis官网也没有提供Windows版本的安装包&#xff0c;因此课程中我们会基于Linux系统来安装Redis。 此处选择Linux版本为Centos7。 Redis的官方网站地址&#xff1a;https://redis.io/ Redis的安装 切换…

TCP协议中的传输控制机制图文详解「重传机制」「流量控制」「拥塞控制」

目录 TCP重传机制 超时重传 快速重传 SACK 方法 Duplicate SACK TCP 流量控制 滑动窗口 累积确认 窗口大小由哪一方决定&#xff1f; 接收窗口和发送窗口的大小是相等的吗&#xff1f; 流量控制 窗口关闭的后果 糊涂窗口综合症 TCP拥塞处理 为什么要有拥塞控制呀&#xff0c;不…

如何在WSL中的ubuntu编译Linux内核并且安装使用ebpf?

如何在WSL中的ubuntu编译Linux内核并且安装使用ebpf? 步骤1 编译安装内核获取源码修改配置编译编译成功后配置重启WSL测试 步骤2 安装bcc安装依赖下载bcc&#xff0c;编译测试 环境: wsl2windows 11 步骤1 编译安装内核 去https://kernel.org/找你想要的版本&#xff0c; …

119.设计链表(力扣)

代码解决 class MyLinkedList { public:// 定义链表节点结构体struct LinkedNode {int val;LinkedNode* next;LinkedNode(int val):val(val), next(nullptr){}};MyLinkedList() {dummyhead new LinkedNode(0);size0;}int get(int index) {if (index > (size - 1) || index…

从点云创建 DSM:网格化和可视化实用指南

今天我将向您展示如何从点云创建数字表面模型&#xff08;DSM&#xff09;。首先&#xff0c;我们将尝试了解 DSM 是什么&#xff0c;然后我们将进入讨论的更实际部分。 什么是 DSM&#xff1f; DSM 是一个描述表面及其表面所有内容的模型。现在&#xff0c;为了更清楚地了解…

学习JavaEE的日子 Day28 异常,多线程

Day28 1.异常机制 1.1 异常概念 异常是程序在运行期发生的不正常的事件&#xff0c;它会打断指令的正常执行流程。 设计良好的程序应该在异常发生时提供处理这些不正常事件的方法&#xff0c;使程序不会因为异常的发生而阻断或产生不可预见的结果。 ​ Java语言使用异常处理机…

LabVIEW柴油机安保监控系统

LabVIEW柴油机安保监控系统 随着航运业的快速发展&#xff0c;确保船舶柴油机的安全稳定运行变得尤为重要。船舶柴油机故障不仅会导致重大的经济损失&#xff0c;还可能危及人员安全和环境。设计并开发了一套基于LabVIEW平台的柴油机安保监控系统&#xff0c;旨在通过实时监控…

Python数学建模-2.9Matplotlib库

Matplotlib库是Python中一个非常流行的绘图库&#xff0c;它提供了大量的绘图工具&#xff0c;可以生成各种类型的静态、动态、交互式的图表。Matplotlib的设计初衷是为了与NumPy配合使用&#xff0c;从而提供一个强大的数学绘图工具。 1.Matplotlib的主要特点 丰富的图表类型…

Ubuntu18.04显示--有线连接未托管

引用: Ubuntu18.04连不网 报"有线连接未托管"_ubuntu20.04以太网未托管-CSDN博客 正文 虚拟机环境配置&#xff1a; VirtaualBox Ubuntu18.04桌面版 问题现象&#xff1a; Ubuntu18.04虚拟机的桌面上提示“有线连接未托管”&#xff0c;虚拟机不能上网&#xf…

抖音视频批量下载软件可导出视频分享链接|手机网页视频提取|视频爬虫采集工具

解锁抖音视频无水印批量下载新姿势&#xff01; 在快节奏的生活中&#xff0c;抖音作为时下最热门的短视频平台之一&#xff0c;吸引着广大用户的目光。而如何高效地获取喜欢的视频内容成为了许多人关注的焦点。Q:290615413现在&#xff0c;我们推出的抖音视频批量下载软件&…