23种设计模式-创建型模式-抽象工厂

文章目录

  • 简介
  • 场景
  • 问题
    • 1. 风格一致性失控
    • 2. 对象创建硬编码
    • 3. 产品族管理失效
  • 解决
  • 总结

简介

抽象工厂是一种创建型设计模式,可以生成相关对象系列,而无需指定它们的具体类。

场景

假设你正在写一个家具店模拟器。
你的代码这些类组成:

  1. 相关产品系列,例如:椅子 + 沙发 + 咖啡桌。
  2. 此系列有多种风格。例如,椅子 + 沙发 + 咖啡桌系列有以下风格:现代、维多利亚、装饰艺术。

在这里插入图片描述
你需要一种方法来创建家具对象,确保它们与同一风格的其他对象相匹配。如果客户收到风格不匹配的家具,他们就会非常生气。

在这里插入图片描述
另外,在向程序添加新产品或产品系列时,你也不想更改现有代码。
按照惯例,大家一开始会怎么实现?

// 直接实例化具体家具类导致风格混杂
class Client {public void createLivingRoom() {// 混合使用不同风格组件(致命错误)Chair modernChair = new ModernChair();Sofa victorianSofa = new VictorianSofa();CoffeeTable artDecoTable = new ArtDecoCoffeeTable();modernChair.sit();victorianSofa.lieDown();artDecoTable.placeMagazine();}
}// 具体产品实现
class ModernChair extends Chair {public void sit() { System.out.println("Modern chair sitting"); } 
}class VictorianSofa extends Sofa {public void lieDown() { System.out.println("Victorian sofa relaxing"); }
}class ArtDecoCoffeeTable extends CoffeeTable {public void placeMagazine() { System.out.println("ArtDeco table placement"); }
}

问题

1. 风格一致性失控

客户端直接创建不同风格的对象(现代椅子+维多利亚沙发)
╭── 问题场景 ──╮
用户订单要求"维多利亚风格客厅"时:
┌─────────────────┬──────────────────────┐
│ 预期组合 │ 实际可能创建的组合 │
├─────────────────┼──────────────────────┤
│ VictorianChair │ VictorianChair │
│ VictorianSofa │ ModernSofa ←不匹配 │
│ VictorianTable │ ArtDecoTable ←灾难性 │
└─────────────────┴──────────────────────┘
结果:客户收到风格冲突的家具套装

2. 对象创建硬编码

每当新增风格时(如新增ArtDeco),强制修改所有客户端代码


// 新增风格场景产生连锁修改
class Client {// 必须添加新分支判断public void createSet(String style) {if ("ArtDeco".equals(style)) { // 破坏开放封闭原则chair = new ArtDecoChair(); // 需要新增多个类引用sofa = new ArtDecoSofa();}}
}

3. 产品族管理失效

缺乏统一约束机制,易出现类型错误

// 错误将现代餐桌与维多利亚椅组合(类型系统无法阻止)
FurnitureSet set = new FurnitureSet(new ModernChair(),new VictorianDiningTable() // 应该抛出异常但现有代码无法约束
);

解决

抽象工厂模式建议的第一件事就是明确声明产品系列中每个不同产品的接口(例如,椅子、沙发或咖啡桌)。然后,让所有风格的产品都实现这些接口。例如,所有风格的椅子都可以实现 Chair 接口;所有风格的咖啡桌都可以实现 CoffeeTable 接口,等等。

在这里插入图片描述

// 接口约束产品规格
public interface Chair {void sit();
}public interface Sofa {void lieDown();
}public interface CoffeeTable {void placeItem();
}
// 确保现代系列组件统一
public class ModernChair implements Chair {@Overridepublic void sit() { System.out.println("Modern chair designed seating"); }
}public class ModernSofa implements Sofa {@Overridepublic void lieDown() { System.out.println("Modern sofa clean lines design"); }
}public class ModernCoffeeTable implements CoffeeTable {@Overridepublic void placeItem() { System.out.println("Modern geometric table surfaces"); }
}// 保证维多利亚风格一致性
public class VictorianChair implements Chair {@Overridepublic void sit() { System.out.println("Classic carved wood chair"); }
}public class VictorianSofa implements Sofa {@Overridepublic void lieDown() { System.out.println("Antique fabric sofa"); }
}public class VictorianCoffeeTable implements CoffeeTable {@Overridepublic void placeItem() { System.out.println("Ornate marble-top table"); }
}

下一步是声明抽象工厂(接口),其中包含特定产品系列所有产品的创建方法列表(例如,createChair、createSofa 和 createCoffeeTable)。这些方法必须返回我们之前定义的抽象产品类型接口:Chair、Sofa、CoffeeTable 等等。对于产品的每种风格,我们基于 AbstractFactory 接口创建一个单独的工厂类。这个工厂类是返回特定类型产品的类。例如,ModernFurnitureFactory 只能创建 ModernChair、ModernSofa 和 ModernCoffeeTable 对象。
在这里插入图片描述

public interface FurnitureFactory {Chair createChair();Sofa createSofa();CoffeeTable createCoffeeTable();
}// 现代风格产品线工厂
public class ModernFactory implements FurnitureFactory {@Overridepublic Chair createChair() { return new ModernChair(); }@Overridepublic Sofa createSofa() { return new ModernSofa(); }@Overridepublic CoffeeTable createCoffeeTable() { return new ModernCoffeeTable(); }
}// 维多利亚风格产品线工厂
public class VictorianFactory implements FurnitureFactory {@Overridepublic Chair createChair() { return new VictorianChair(); }@Overridepublic Sofa createSofa() { return new VictorianSofa(); }@Overridepublic CoffeeTable createCoffeeTable() { return new VictorianCoffeeTable(); }
}

客户端代码必须通过抽象接口来和工厂、产品协作。这样就可以更改传给客户端代码的工厂的类型以及客户端代码接收的产品风格,而不破坏实际的客户端代码。
假设客户希望工厂生产一把椅子。客户不必知道工厂的类别,也不必关心它得到的椅子是什么类型。无论是现代风格还是维多利亚风格的椅子,客户都必须使用抽象Chair接口以相同的方式处理所有椅子。唯一知道的是sitOn方法。此外,无论返回哪种椅子,它总是与同一工厂对象生产的沙发或咖啡桌风格相匹配。

public class FurnitureStore {private FurnitureFactory factory;// 动态绑定具体工厂public FurnitureStore(FurnitureFactory factory) {this.factory = factory;}public void showcaseSet() {Chair chair = factory.createChair();Sofa sofa = factory.createSofa();CoffeeTable table = factory.createCoffeeTable();System.out.println("展示完整风格套件:");chair.sit();sofa.lieDown();table.placeItem();}
}// 使用示例
public class Main {public static void main(String[] args) {// 创建现代风格商店FurnitureStore modernStore = new FurnitureStore(new ModernFactory());modernStore.showcaseSet();// 创建维多利亚风格商店FurnitureStore victorianStore = new FurnitureStore(new VictorianFactory());victorianStore.showcaseSet();}
}

还有一件事需要明确:如果客户端只接触抽象接口,那是什么创建了实际的工厂对象(即new ModernFactory())?通常,应用程序在初始化阶段创建一个具体的工厂对象。在这之前,应用程序必须根据配置或环境设置选择工厂类型。

总结

在这里插入图片描述

  1. 抽象产品(Abstract Prod­uct):构成所有产品的一组接口。
  2. 具体产品(Con­crete Prod­uct):抽象产品的多种不同类型实现。所有风格产品(维多利亚/现代)都必须实现相应的抽象产品(椅子/沙发)。
  3. 抽象工厂(Abstract Fac­to­ry)接口:声明了一组创建各种抽象产品的方法。
  4. 具体工厂(Con­crete Fac­to­ry):实现抽象工厂的产品创建方法。每个具体工厂都对应特定风格的产品,且只能创建这一种风格的产品。
  5. 尽管具体工厂会对具体产品进行初始化,它的创建方法必须返回相应的抽象产品。只有这样,使用工厂类的客户端代码就不会与工厂创建的特定风格产品耦合。客户端(Client)只需通过抽象接口调用工厂和产品对象,就可以跟任何具体工厂/产品进行交互。

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

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

相关文章

案例:网络命名空间模拟隔离主机场景

场景描述 假设我们需要在同一台物理机上模拟两台独立的主机(Host A 和 Host B),它们分别位于不同的网络命名空间中,并通过虚拟以太网对(veth pair)进行通信。目标是展示网络命名空间的隔离性和跨命名空间的…

新闻发布时间抽取(二)

1. 再论抽取方法 在前一期实验中,对gne组件进行分析和完善,对三种时间抽取的方法进行了实验对比。 在对抽取结果进行个例分析的过程中,我发现此前实验存在几个问题: 抽取的1000篇新闻存在一定的重复,经过ID去重大约减…

算法基础——栈

一、栈的概念 栈是⼀种只允许在⼀端进⾏数据插⼊和删除操作的线性表。 进⾏数据插⼊或删除的⼀端称为栈顶,另⼀端称为栈底。不含元素的栈称为空栈。进栈就是往栈中放⼊元素,出栈就是将元素弹出栈顶。 二、栈的模拟实现 1. 创建 本质还是线性表&#…

Android11至15系统定制篇

Android 11至15系统定制核心要点解析 一、Android 11关键定制特性 ‌分区存储强制化‌ 公共目录(如Downloads、Pictures)与应用专属目录分离,应用更新后无法通过requestLegacyExternalStorage绕过限制‌1。需申请MANAGE_EXTERNAL_STORAGE权限…

macOS 使用 enca 识别 文件编码类型(比 file 命令准确)

文章目录 macOS 上安装 enca基本使用起因 - iconv关于 enca安装 Encaenca & enconv 其它用法 macOS 上安装 enca brew install enca基本使用 enca filepath.txt示例 $ enca 动态规划算法.txt [0] Simplified Chinese National Standard; GB2312CRLF line terminat…

线段树与扫描线 —— 详解算法思想及其C++实现

目录 一、线段树(Segment Tree) 基本概念 结构 操作 示例代码 二、扫描线(Sweep Line) 基本概念 应用场景 示例代码(矩形面积并集) 三、总结 一、线段树(Segment Tree) 基本…

汇编代码中嵌入回调函数的优化说明

一、概述 在 PowerPC 的汇编代码中,我们需要实现调用 C 函数(例如回调函数),并传递参数。本文将详细介绍如何通过一系列步骤完成这一目标,包括代码示例和详细的注释。 二、调用 C 函数的基本步骤及代码 1. 保存工作寄…

Uni-App 双栏联动滚动组件开发详解 (电梯导航)

本文基于提供的代码实现一个左右联动的滚动组件&#xff0c;以下是详细的代码解析与实现原理说明&#xff1a; <!--双栏联动滚动组件 - 技术解析功能特性&#xff1a;1. 左侧导航栏与右侧内容区双向联动2. 自适应容器高度3. 平滑滚动定位4. 动态内容位置计算 --> <te…

软考复习-传输介质与编码

传输介质 双绞线 传输距离100一200m&#xff0c;即网线&#xff0c;有多种分类 UTP非屏蔽双绞线 STP屏蔽双绞线 线序标准有两种为&#xff1a; T568A标准&#xff1a;绿白、绿、橙白、蓝、蓝白、橙、棕白、棕 T568B标准&#xff1a;橙白、橙、绿白、蓝、蓝白、绿、棕白、…

论文阅读笔记:Denoising Diffusion Probabilistic Models (3)

论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (1) 论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (2) 论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (3) 4、损失函数逐项分析 可以看出 L L L总共分为了3项…

PyTorch 面试题及参考答案(精选100道)

目录 PyTorch 的动态计算图与 TensorFlow 的静态计算图有何区别?动态图的优势是什么? 解释张量(Tensor)与 NumPy 数组的异同,为何 PyTorch 选择张量作为核心数据结构? 什么是 torch.autograd 模块?它在反向传播中的作用是什么? 如何理解 PyTorch 中的 nn.Module 类?…

#C8# UVM中的factory机制 #S8.1.4# 约束的重载

今天,复习一下《UVM实战》一书中的 关于约束的重载 章节学习。 一 问题引导 文件:src/ch8/section8.1/8.1.2/rand_mode/my_transaction.sv4 class my_transaction extends uvm_sequence_item; …17 constraint crc_err_cons{18 crc_err == 1b0;19 }20 const…

空调遥控器低功耗单片机方案

RAMSUN空调遥控器采用先进的32位低功耗单片机作为核心控制器&#xff0c;通过优化软件算法和硬件设计&#xff0c;实现了空调遥控器的低功耗运行。单片机集成了多种功能模块&#xff0c;包括红外发射、按键扫描、电源管理等&#xff0c;有效降低了整体功耗。同时&#xff0c;该…

结构型——代理模式

结构型——代理模式 代理模式指的是通过创建一个代理来控制对原始对象的访问。代理在客户端与实际对象之间充当“中介” 特点 访问控制&#xff1a;代理对象可以控制对实际对象的访问&#xff0c;从而实现对访问权限的控制。延迟加载&#xff1a;代理对象可以在实际对象被调…

【算法】常见排序算法(插入排序、选择排序、交换排序和归并排序)

文章目录 前言一、排序概念及常见排序算法框图1.排序概念2.常见排序算法框图 二、实现比较排序算法1.插入排序1.1 直接插入排序1.2 希尔排序 2.选择排序2.1 直接选择排序2.2 堆排序 3.交换排序3.1 冒泡排序3.2 快速排序3.2.1 hoare版本3.2.2 挖坑法3.2.3 lomuto前后指针 3.3 快…

Go语言分布式锁实战:dlock助力构建高并发稳定系统

在构建分布式系统时&#xff0c;一个常见且棘手的问题便是资源竞争和数据一致性问题。分布式锁作为一种常用的解决方案&#xff0c;在多个进程或节点之间协调访问共享资源时显得尤为重要。今天&#xff0c;我们将介绍一款分布式锁库——dlock&#xff0c;并通过详细的使用示例带…

算法方法快速回顾

&#xff08;待修改&#xff09; 目录 1. 双指针2. 滑动窗口理论基础 3. 二分查找3. 二分查找理论基础 4. KMP5. 回溯算法6. 贪心算法7. 动态规划7.1. 01背包7.2. 完全背包7.3. 多重背包 8. 单调栈9. 并查集10. 图论10.1. 广度优先搜索&#xff08;BFS&#xff09;10.2. 深度优…

深度学习:让机器学会“思考”的魔法

文章目录 引言&#xff1a;从“鹦鹉学舌”到“举一反三”一、深度学习是什么&#xff1f;1. 定义&#xff1a;机器的“大脑”2. 核心思想&#xff1a;从数据中“悟”出规律 二、深度学习的“大脑”结构&#xff1a;神经网络1. 神经元&#xff1a;深度学习的基本单元2. 神经网络…

电动自行车/电动工具锂电池PCM方案--SH367003、SH367004、SH79F329

在消费电子系统中&#xff0c;如手机电池包&#xff0c;笔记本电脑电池包等&#xff0c;带有控制IC、功率MOSFETFE管以及其他电子元件的电路系统称为电池充放电保护板Protection Circuit Module &#xff08;PCM&#xff09;&#xff0c;而对于动力电池的电池管理系统&#xff…

补码详细分析

补码引入 举一个生活化的例子 假设由一个挂钟&#xff0c;它只能顺时钟调时间&#xff0c;那么它调时间就分成了一下两种情况 正好顺时针调就能调好 如&#xff1a;时针从5调到9需要逆时针调才能调好 如&#xff1a;时针从10调到7 在上面的情况中1是不用处理的&#xff0c;2…