设计模式之结构型设计模式(二):工厂模式 抽象工厂模式 建造者模式

工厂模式 Factory

1、什么是工厂模式

工厂模式旨在提供一种统一的接口来创建对象,而将具体的对象实例化的过程延迟到子类或者具体实现中。有助于降低客户端代码与被创建对象之间的耦合度,提高代码的灵活性和可维护性。

定义了一个创建对象的接口,但不负责具体对象的实例化。而是将实例化的责任交给它的子类或者具体实现,这种模式包括抽象工厂、工厂方法和简单工厂等不同形式。

2、为什么使用工厂模式

  1. 降低耦合度:工厂模式将客户端代码与具体的类实现分离,降低它们之间的耦合度,客户端只需要知道工厂接口或者抽象类,而无需关心具体的实现细节。
  2. 可扩展性:当需要添加新的产品类时,只需要创建一个新的具体工厂类和产品类,而不需要修改已有的代码,有助于系统的可扩展性,符合开闭原则。
  3. 隐藏实现细节:工厂模式将对象的创建过程封装在工厂类中,客户端无需知道对象的具体创建细节,有助于隐藏实现细节,提高系统的安全性。

3、如何实现工厂模式

简单工厂模式

简单工厂模式是工厂模式的一种简化形式,包含了一个具体工厂类,负责创建产品的对象。

// 抽象产品类
interface Product {void display();
}// 具体产品类A
class ConcreteProductA implements Product {@Overridepublic void display() {System.out.println("Product A");}
}// 具体产品类B
class ConcreteProductB implements Product {@Overridepublic void display() {System.out.println("Product B");}
}// 简单工厂类
class SimpleFactory {public static Product createProduct(String type) {switch (type) {case "A":return new ConcreteProductA();case "B":return new ConcreteProductB();default:throw new IllegalArgumentException("Invalid product type");}}
}// 客户端代码
public class Client {public static void main(String[] args) {Product productA = SimpleFactory.createProduct("A");productA.display();  // Output: Product AProduct productB = SimpleFactory.createProduct("B");productB.display();  // Output: Product B}
}
工厂方法模式

工厂方法模式定义了一个创建产品的接口,具体的产品创建由其子类负责实现。

// 抽象产品类
interface Product {void display();
}// 具体产品类A
class ConcreteProductA implements Product {@Overridepublic void display() {System.out.println("Product A");}
}// 具体产品类B
class ConcreteProductB implements Product {@Overridepublic void display() {System.out.println("Product B");}
}// 抽象工厂接口
interface Factory {Product createProduct();
}// 具体工厂类A
class ConcreteFactoryA implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductA();}
}// 具体工厂类B
class ConcreteFactoryB implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductB();}
}// 客户端代码
public class Client {public static void main(String[] args) {Factory factoryA = new ConcreteFactoryA();Product productA = factoryA.createProduct();productA.display();  // Output: Product AFactory factoryB = new ConcreteFactoryB();Product productB = factoryB.createProduct();productB.display();  // Output: Product B}
}

4、是否存在缺陷和不足

  1. 类爆炸:随着产品类的增加,工厂类的数量也会呈现指数级增长,导致类的爆炸,不利于系统的维护。
  2. 违背开闭原则:每次添加新产品都需要修改工厂类,违背了开闭原则,当有新产品加入时,必须修改所有工厂类。

5、如何缓解缺陷与不足

  1. 使用抽象工厂模式:抽象工厂模式将一组相关的产品封装在一起,形成一个产品族,每个具体工厂负责创建一族产品,缓解了类的爆炸问题,
  2. 依赖注入:将工厂的创建过程交给外部来管理,通过依赖注入的方式,避免了工厂类的频繁修改。
  3. 使用反射:可以使用反射机制,动态地创建产品对象,从而减少了工厂类的数量。

通过以上缓解措施,可以在一定程度上提高工厂模式的灵活性和可维护性,使其更好地适应变化。在实际应用中,根据具体场景选择合适的工厂的模式,并结合其他设计模式,以达到代码的清晰和可扩展。

抽象工厂模式 Abstract Factory

1、什么是抽象工厂模式

抽象工厂模式提供了一个接口,用于创建与产品家族相关的对象,无需制定具体类,有助于确保创建的对象能够相互配合使用,而无需指定具体的类。

抽象工厂模式提供了一种将一组相关的产品组合成一个家族的方式,而不必指定具体的类,通过引入抽象工厂接口和一组具体工厂类,为每个产品提供一个独立的工厂,从而使系统更具灵活性。

2、为什么用抽象工厂模式

  1. 产品家族一致性:抽象工厂模式确保创建的对象相互之间是兼容的,属于同一产品家族,有助于保持系统的一致性。
  2. 易于替换:由于客户端只依赖于抽象接口,而不直接依赖具体类,因此可以轻松替换整个产品家族的实现,而无需修改客户端代码。
  3. 隐藏实现细节:客户端无需知道具体产品的实现细节,只需要了解抽象工厂接口,从降低了系统的复杂度。

3、如何实现抽象工厂模式

示例:图形界面库的抽象工厂模式,包含图形界面库中的按钮和文本框。

// 抽象按钮接口
interface Button {void display();
}// 具体按钮A
class ButtonA implements Button {@Overridepublic void display() {System.out.println("Button A");}
}// 具体按钮B
class ButtonB implements Button {@Overridepublic void display() {System.out.println("Button B");}
}// 抽象文本框接口
interface TextBox {void display();
}// 具体文本框A
class TextBoxA implements TextBox {@Overridepublic void display() {System.out.println("TextBox A");}
}// 具体文本框B
class TextBoxB implements TextBox {@Overridepublic void display() {System.out.println("TextBox B");}
}// 抽象工厂接口
interface GUIFactory {Button createButton();TextBox createTextBox();
}// 具体工厂A
class GUIFactoryA implements GUIFactory {@Overridepublic Button createButton() {return new ButtonA();}@Overridepublic TextBox createTextBox() {return new TextBoxA();}
}// 具体工厂B
class GUIFactoryB implements GUIFactory {@Overridepublic Button createButton() {return new ButtonB();}@Overridepublic TextBox createTextBox() {return new TextBoxB();}
}// 客户端代码
public class Client {public static void main(String[] args) {// 使用工厂A创建按钮和文本框GUIFactory factoryA = new GUIFactoryA();Button buttonA = factoryA.createButton();TextBox textBoxA = factoryA.createTextBox();buttonA.display();  // Output: Button AtextBoxA.display(); // Output: TextBox A// 使用工厂B创建按钮和文本框GUIFactory factoryB = new GUIFactoryB();Button buttonB = factoryB.createButton();TextBox textBoxB = factoryB.createTextBox();buttonB.display();  // Output: Button BtextBoxB.display(); // Output: TextBox B}
}

4、是否存在缺陷和不足

  1. 不易扩展新的产品家族:当需要添加新的产品家族时,需要修改抽象工厂接口及其所有的实现类,违背了开闭原则,使得系统扩展性受限。
  2. 复杂性增加:随着产品家族的增加,抽象工厂模式的类和接口数量可能呈现指数级增长,导致系统复杂性增加。

5、如何缓解缺陷和不足

  1. 使用依赖注入:将工厂的创建过程交给外部来管理,通过依赖注入的方式,避免了工厂类的频繁修改。
  2. 使用反射:可以使用反射机制,动态地创建产品对象,从而减少了工厂类和产品类的数量。
  3. 使用配置文件:将产品家族的配置信息放置在配置文件中,通过读取配置文件的方式动态创建工厂和产品对象,提高了系统的灵活性。

建造者模式 Builder

1、什么是建造者模式

建造者模式旨在通过将复杂对象的构造过程分离成多个简单的步骤,使得同样的创建过程可以创建不同的表示,有助于客户端代码能够根据需求选择构建过程的不同组合,以创建不同属性的对象。

将一个复杂对象的构建与其表示分离,使得同样的创建过程可以创建不同的表示,主要包含以下角色:

  • 产品
  • 抽象建造者
  • 具体建造者
  • 指挥者

2、为什么用建造者模式

  1. 分布创建:建造者模式允许按照步骤构建一个复杂对象,使得客户端代码可以选择性地构建对象的不同部分,灵活性更高。
  2. 隔离复杂性:将构建过程在具体的建造者中,客户端无需关心构建的细节,从而降低了系统的复杂性。
  3. 可扩展性:可以通过增加新的具体的建造者类来扩展系统,而不影响已有的客户端代码。

3、如何实现建造者模式

设计实现一个电脑组装的建造者模式

// 产品类
class Computer {private String cpu;private String memory;private String storage;public Computer(String cpu, String memory, String storage) {this.cpu = cpu;this.memory = memory;this.storage = storage;}// Getters...public void display() {System.out.println("Computer Specs: CPU-" + cpu + ", Memory-" + memory + ", Storage-" + storage);}
}// 抽象建造者
interface ComputerBuilder {void buildCPU(String cpu);void buildMemory(String memory);void buildStorage(String storage);Computer getResult();
}// 具体建造者A
class ConcreteBuilderA implements ComputerBuilder {private Computer computer;public ConcreteBuilderA() {this.computer = new Computer("", "", "");}@Overridepublic void buildCPU(String cpu) {computer = new Computer(cpu, computer.getMemory(), computer.getStorage());}@Overridepublic void buildMemory(String memory) {computer = new Computer(computer.getCpu(), memory, computer.getStorage());}@Overridepublic void buildStorage(String storage) {computer = new Computer(computer.getCpu(), computer.getMemory(), storage);}@Overridepublic Computer getResult() {return computer;}
}// 具体建造者B
class ConcreteBuilderB implements ComputerBuilder {private Computer computer;public ConcreteBuilderB() {this.computer = new Computer("", "", "");}@Overridepublic void buildCPU(String cpu) {computer = new Computer(cpu, computer.getMemory(), computer.getStorage());}@Overridepublic void buildMemory(String memory) {computer = new Computer(computer.getCpu(), memory, computer.getStorage());}@Overridepublic void buildStorage(String storage) {computer = new Computer(computer.getCpu(), computer.getMemory(), storage);}@Overridepublic Computer getResult() {return computer;}
}// 指挥者
class Director {public void construct(ComputerBuilder builder) {builder.buildCPU("Intel i5");builder.buildMemory("8GB");builder.buildStorage("256GB SSD");}
}// 客户端代码
public class Client {public static void main(String[] args) {// 使用建造者A构建电脑ComputerBuilder builderA = new ConcreteBuilderA();Director director = new Director();director.construct(builderA);Computer computerA = builderA.getResult();computerA.display();// 使用建造者B构建电脑ComputerBuilder builderB = new ConcreteBuilderB();director.construct(builderB);Computer computerB = builderB.getResult();computerB.display();}
}

4、是否存在缺陷和不足

  1. 指挥者的变动:如果产品的构建步骤发生变化,指挥者类的代码也需要修改,违背了开闭原则。
  2. 不够灵活:当产品的构建步骤很多且相互关联时,建造者模式可能变得复杂且不够灵活。

5、如何缓解缺陷和不足

  1. 使用链式调用:在具体建造者中使用链式调用,使得客户端代码更加简洁,且不容易受到构建步骤变动的影响。
  2. 增加产品的变种:当产品的构建步骤较为复杂时,可以考虑增加产品的变种,以适应不同的构建需求。
  3. 使用反射和配置文件:可以通过反射机制和配置文件来动态配置产品的构建过程,提高系统的灵活性。

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

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

相关文章

持续集成交付CICD:Jenkins使用GitLab共享库实现基于SaltStack的CD流水线部署前后端应用

目录 一、实验 1.Jenkins使用GitLab共享库实现基于SaltStack的CD流水线部署前后端应用 2.优化共享库代码 二、问题 1.Jenkins手动构建后端项目流水线报错 一、实验 1.Jenkins使用GitLab共享库实现基于SaltStack的CD流水线部署前后端应用 (1)GitLa…

【Jenkins】Centos环境安装Jenkins(通过rpm安装)

在Centos操作系统中通过rpm安装Jenkins 参考官网 https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos 1、下载安装Jdk17 下载安装 # 更新您的系统,不一定需要 # sudo yum -y update # 安装将用于下载 Java 17 二进制文件的 wget 命令行工具。 s…

Python基础01-环境搭建与输入输出

零、文章目录 Python基础01-环境搭建与输入输出 1、Python概述 (1)为什么要学习Python 技术趋势:Python自带明星属性,热度稳居编程语言界前三 简单易学:开发代码少,精确表达需求逻辑;33个关…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 黑白老照片上色修复

在这个时代,我们习惯于拥有高清、色彩丰富的照片,然而,那些古老的黑白色老照片由于年代的久远,往往会出现模糊、破损等现象。 那么今天要给大家介绍的是,用 Stable Diffusion 来修复老照片。 前段时间 ControlNet 的除了上线了“IP-Adapter”模型以外还增加另一个…

【ArkTS】生命周期

页面生命周期 通常Entry修饰的组件称为页面,其拥有页面生命周期 onPageShow:页面每次显示时触发。onPageHide:页面每次隐藏时触发(通常是路由跳转到其他页面了)。onBackPress:当用户点击返回按钮时时触发…

【MySQL】库的操作

数据库的操作 所有操作命令: //创建数据库 create database 数据库名字 charsetutf8 collate utf8_general_ci //查看字符集 show charset; //查看校验规则 show collation; //查看所有的数据库。 show databases; //显…

springboot+jdbcTemplate+sqlite编程示例——以沪深300成分股数据处理为例

引言 我们在自己做一些小的项目或者小的数据处理分析的时候,很多时候是不需要用到mysql这样的大型数据库,并且也不需要用到maven这样很重的框架的,取而代之可以使用jdbcTemplatesqlite这样的组合。 本文就介绍一下使用springbootjdbcTempla…

LeetCode-反转链表问题

1.反转链表 题目描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 思路: 反转链表也就是链表倒置,我一直以来的办法就是先建立一个头节点,之后再遍历链表来进行头插。 代码&#xff1…

【深度学习】强化学习(三)强化学习的目标函数

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略(Policy)4、马尔可夫决策过程5、强化学习的目标函数1. 总回报(Return)2. 折扣回报(Discounted Return)a. 折扣率b. 折扣回报的定义 3.…

【web安全】密码爆破讲解,以及burp的爆破功能使用方法

前言 菜某总结,欢迎指正错误进行补充 密码暴力破解原理 暴力破解实际就是疯狂的输入密码进行尝试登录,针对有的人喜欢用一些个人信息当做密码,有的人喜欢用一些很简单的低强度密码,我们就可以针对性的生成一个字典,…

NO-IOT翻频,什么是翻频,电信为什么翻频

1.1 翻频迁移最终的目的就是减少网络的相互干扰,提供使用质量. 1.2 随着与日俱增的网络规模的扩大,网内干扰已成了影响网络的质量标准之一,为了保障电信上网体验,满足用户日益增长的网速需求,更好的服务客户,电信针对…

CSS学习

CSS学习 1. 什么是css?2.css引入方式2.1 内嵌式2.2 外联式2.3 行内式2.4 引入方式特点 3. 基础选择器3.1 标签选择器3.2 类选择器3.3 id选择器3.4 通配符选择器 4. 文字基本样式4.1 字体样式4.1.1 字体大小4.1.2 字体粗细4.1.3 倾斜4.1.4 字体4.1.5 字体font相关属性连写 4.2 …

TrustZone之总线请求

接下来,我们将查看系统中的总线请求者,如下图所示: 系统中的A型处理器具有TrustZone感知,并在每个总线访问中发送正确的安全状态。然而,大多数现代SoC还包含非处理器总线请求者,例如GPU和DMA控制器。 与完成…

[蓝桥杯刷题]合并区间、最长不连续子序列、最长不重复数组长度

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录: 成功的关键在于对目标的持久追求。 ⭐个人主页:欧_aita ψ(._. )>⭐个人专栏: 数据结构与算法 数据库 文章目录 前言合并区间问题📕现实应用大致思路代码实现代码讲解 最长不连续子序列&a…

获取和移除cookie的方法

下载npm的cookie插件, 在utils.js文件中引入插件: 封装原始的Cookies.get()方法: 在xxxx.vue文件中引入方法: 使用getCookie方法获取cookie: 封装 移除cookie: export const removeCookie name>{ const options { path: /, domain: xxx.com }; Cookies.remove(name, opti…

音频DAC,ADC,CODEC的选型分析,高性能立体声

想要让模拟信号和数字信号顺利“交往”,就需要一座像“鹊桥”一样的中介,将两种不同的语言转变成统一的语言,消除无语言障碍。这座鹊桥就是转换器芯片,也就是ADC芯片。ADC芯片的全称是Analog-to-Digital Converter, 即模拟数字转换…

二叉搜索树的实现

本文旨在讲解如何编写一颗二叉搜索树,包括基本的增删查改的操作。 目录 一、二叉搜索树的概念 ​编辑二、二叉搜索树的编写 2.1节点的编写 2.2节点的插入 2.3节点的查找 2.4节点的删除 三、二叉搜索树的应用 四、 二叉搜索树的性能分析 五、完整代码 一、…

mysql EXPLAIN命令的输出列简介

MySQL :: MySQL 8.2 Reference Manual :: 8.8.2 EXPLAIN Output Format explain命令提供了mysql数据库如何执行SQL语句的信息,可以跟 SELECT, DELETE, INSERT, REPLACE, UPDATE, 和 TABLE一起使用。 explain命令可能输出多行,每行涉及一个表 。 先来看…

Unity升级到2022版本后,打开Spine会卡住

1)Unity升级到2022版本后,打开Spine会卡住 2)iPhone在同时播放多个音效的时候会压低某些音源的音量 3)在Y77手机上出现IMGSRV:GetMainShaderConstantBufferBaseAddress: Unsupported 4)UE4打包后在部分安卓机型出现“花…

理解linux中反向映射与应用

反向映射的作用是根据物理页,找到全部相关进程的vma。 主要有两个结构,anon_vma_chain链表,和 anon_vma->rb_root红黑树 打个不恰当的比喻:可以简单认为,红黑树是用来读的(遍历找全部映射的vm_area&am…