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

工厂模式 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.mzph.cn/news/225514.shtml

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

相关文章

【通用】Linux,VSCode,IDEA,Eclipse等资源相对位置

正文 不论是 IDEA、Linux、VSCode、cmd等等吧,都遵循这个规则: 如果以斜杠开头,表示从根开始找: IDEA的根是classpath(classpath就是项目被编译后,位于 target下的 classes文件夹,或者位于ta…

软实力篇---第三篇

系列文章目录 文章目录 系列文章目录前言一、专业技能怎么写二、排版注意事项三、其他一些小tips前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、专业技能怎么…

自然语言处理阅读第一弹

Transformer架构 encoder和decoder区别 Embeddings from Language Model (ELMO) 一种基于上下文的预训练模型,用于生成具有语境的词向量。原理讲解ELMO中的几个问题 Bidirectional Encoder Representations from Transformers (BERT) BERT就是原生transformer中的Encoder两…

爬虫akamai案例:DHL国际物流

声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关 一、Akamai简介 Akamai是一家提供内容传递网络(CDN)和云服务的公司。CDN通过将内容分发到全球各地的服…

RK3568全国产化多网口板卡,支持内置UPS模块,支持麒麟翼辉国产系统

信迈XM-3568-01主板采用瑞芯微RK3568四核Cortex-A55 处理器,主频最高可达2.0GHz,效能有大幅提升最高可配8GB内存容量,频率高达1600MHz;支持全链路ECC,让数据更安全可靠配置双千兆自适应RJ45以太网口,并扩展…

带你学C语言~指针(1)

Hello,CSDN的各位家人们,你们好啊!今天,小赵要给大家分享的C语言知识是指针,相信不少家人们都或多或少被指针搞得晕头转向,小赵一开始也是,但后来小赵经过不断地努力学习,终于将这里面的知识弄懂…

Pearson、Spearman 相关性分析使用

介绍 Pearson 积差相关系数衡量了两个定量变量之间的线性相关程度。 用来衡量两个数据集的线性相关程度,仅当一个变量的变化与另一个变量的比例变化相关时,关系才是线性的。 Spearman等级相关系数则衡量分级定序变量之间的相关程度。斯皮尔曼相关系数不…

汽车充电协议OpenV2G的平替cbexigen!!

纵所周知,开源欧规协议 CCS 的 OpenV2G 协议不支持 ISO15118-20:2022 协议,并且软件维护者已经明确不在进行该软件的维护。 前几天在 Github 上冲浪发现了一个宝藏开源项目,完美的实现的 OpenV2G 的 Exidizer 工具的功能:cbexigen…

Centos开机进入grub命令行模式进入不了操作系统

环境:没有linux命令,没有initrd命令,没有init6命令 由于删除了/boot/efi/EFI/centos/grub.cfg ,重启服务器后,无法进入原来正常的系统,进入了grub命令行界面 备注:对于centos7/8/openEuler: 如果…

数据结构之排序

目录 ​ 1.常见的排序算法 2.插入排序 直接插入排序 希尔排序 3.交换排序 冒泡排序 快速排序 hoare版本 挖坑法 前后指针法 非递归实现 4.选择排序 直接选择排序 堆排序 5.归并排序 6.排序总结 一起去,更远的远方 1.常见的排序算法 排序:所…

Unity实现GoF23种设计模式

文章目录 Unity实现GoF23种设计模式概要一、创建型模式(Creational Patterns):二、结构型模式(Structural Patterns):三、行为型模式(Behavioral Patterns):Unity实现GoF23种设计模式概要 GoF所提出的23种设计模式主要基于以下面向对象设计原则: 对接口编程而不是对实…

RocketMQ系统性学习-RocketMQ领域模型及Linux下单机安装

MQ 之间的对比 三种常用的 MQ 对比,ActiveMQ、Kafka、RocketMQ 性能方面: 三种 MQ 吞吐量级别为:万,百万,十万消息发送时延:毫秒,毫秒,微秒可用性:主从,分…

PyQt6 QFrame分割线控件

锋哥原创的PyQt6视频教程: 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计46条视频,包括:2024版 PyQt6 Python桌面开发 视频教程(无废话版…

消除非受检警告

在Java中,有一些情况下编译器会生成非受检警告(Unchecked Warnings)。这些警告通常与泛型、类型转换或原始类型相关。消除这些警告可以提高代码的可读性和安全性。以下是一些常见的非受检警告以及如何消除它们的例子: 1. 泛型类型…

STM32-UART-DMA HAL库缓冲收发

文章目录 1、说明1.1、注意事项:1.2、接收部分1.3、发送部分 2、代码2.1、初始化2.2、缓冲接收2.3、缓冲发送2.4、格式化打印 1、说明 1.1、注意事项: HAL库的DMA底层基本都会默认开启中断使能,如果在STM32CubeMx禁用了中断相关的功能&…

人工智能与量子计算:开启未知领域的智慧之旅

导言 人工智能与量子计算的结合是科技领域的一场创新盛宴,引领我们进入了探索未知领域的新时代。本文将深入研究人工智能与量子计算的交汇点,探讨其原理、应用以及对计算领域的深远影响。 量子计算的崛起为人工智能领域注入了新的活力,开启了…

利用canvas封装录像时间轴拖动(uniapp),封装上传uniapp插件市场

gitee项目地址,项目是一个空项目,其中包含了封装的插件,自己阅读,由于利用了canvas所以在使用中暂不支持.nvue,待优化; 项目也是借鉴了github上的一个项目,timeline-canvas,​​​​​​​ ​​​​​​​

GPT-4V被超越?SEED-Bench多模态大模型测评基准更新

📖 技术报告 SEED-Bench-1:https://arxiv.org/abs/2307.16125 SEED-Bench-2:https://arxiv.org/abs/2311.17092 🤗 测评数据 SEED-Bench-1:https://huggingface.co/datasets/AILab-CVC/SEED-Bench SEED-Bench-2&…

纽扣电池是什么

纽扣电池 电工电气百科 文章目录 纽扣电池前言一、纽扣电池是什么二、纽扣电池的类别三、纽扣电池的作用原理总结前言 纽扣电池具有易于更换的特点,这使得它们成为许多便携设备的理想电源选择。但是,由于它们较小且外壳易于打开,所以家中有婴幼儿的家庭应特别注意将其放置在…

[css] flex wrap 九宫格布局

<div class"box"><ul class"box-inner"><li>九宫格1</li><li>九宫格2</li><li>九宫格3</li><li>九宫格4</li><li>九宫格5</li><li>九宫格6</li><li>九宫格7&l…