设计模式-抽象工厂模式

在经济学领域中,其主要研究对象(商品)之间根据消费依存关系可分为互补商品或替代商品,其中,互补商品如汽车与汽油、自行车与自行车胎、大饼和香肠、开水和泡面等。在面向对象的代码世界中,不同对象之间也存在这种类似相互依赖的关系【注:这种关系非指定为上面互补关系】,比如特斯拉汽车与新能源、奔驰汽车与92汽油、拖拉机与柴油,这种依赖关系是具有明显的依赖关系的。这意味着如果用户(客户端)获取的汽车对象种类不同,也意味着其所需要的能源种类也存在不同。思考这样一个问题,不同对象之间的依赖关系应该由谁去维护(负责)?

  • 由客户端维护:客户端不适合维护这种依赖关系。如果目标对象发生升级改造,可能会使得这种依赖关系改变,改变就需要所有上游跟随改动,不利于系统稳定性。
  • 由目标创建对象维护:有的同学可能认为能源应该是汽车的属性,因此应该由目标对象维护合适。这是由于上面的例子的特殊性(整体与部分),比如左车门和右车门的个数这种平级对象约束就无法由目标对象维护了。

那我们有没有可能添加一个中间层来负责这种对象依赖关系呢?答案就是抽象工厂模式。

一、抽象工厂模式概念

区别于前面文章讲解的用于封装创建过程的工厂方法模式,抽象工厂模式更关注于创建的不同对象之间的约束关系。因此我们将这些不同对象的创建过程及其之间的约束关系封装于工厂中,客户端仅需要使用该工厂去创建对应需要的对象即可,不需要感知其间的约束关系。这些不同对象在大多数的参考资料中被称之为产品族。这也意味着产品族有多少个对象,那么工厂就会有几个创建对象的方法。
如何理解抽象?抽象,我们通常等同于不可实例化。但是我们要彻底理解抽象的概念,才能理解抽象工厂。在Java中的不可实例化对象实际上是个狭隘理解,抽象应该理解为区别于具体的概念化的东西。如上述中不同对象之间的约束关系,这就是抽象化的概念,进而负责这种关系的工厂也就是抽象工厂了。抽象工厂并非是说抽象工厂模式是实现上使用了抽象类(工厂方法模式也有抽象类,这是依赖倒置原则决定的)。
对于抽象工厂模式理解了“产品族之间的约束关系”“抽象含义”基本就无难点了。抽象工厂模式定义如下:

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定他们的具体类。

前半句就指明了上述的两个概念。后面半句说的是具体产品族中各个对象的具体实现类由具体工厂决定。

二、应用实践

抽象工厂模式相对来说还是有一点理解难度的,本章节将采用一个合适的案例来讲解抽象工厂模式。
案例背景:完成实现文本编辑器的创建和输入法创建在不同操作平台(Windows、Mac)上的实现。我们都知道不同操作系统的文本编辑器必然需要选用合适的输入法才能正常工作使用。因此在系统中会存在不同操作平台下的文本编辑器对象和输入法对象,在创建这些对象的时候一定要处理好他们之间的约束关系,否则就不能正常工作。
具体实现:使用抽象工厂方法模式来创建文本编辑器对象和输入法对象,且这种约束关系由工厂来维护。代码如下:
① 产品接口

public interface TextEditor {void open();void save();
}public interface InputMethod {void type();void select();
}

② 产品具体实现类
Win平台:

public class WindowsTextEditor implements TextEditor {@Overridepublic void open() {System.out.println("Win文本编辑器: Open");}@Overridepublic void save() {System.out.println("Win文本编辑器: Save");}
}public class WindowsInputMethod implements InputMethod {@Overridepublic void type() {System.out.println("Win输入法: Type");}@Overridepublic void select() {System.out.println("Win输入法: Select");}
}

Mac平台:

public class MacTextEditor implements TextEditor {@Overridepublic void open() {System.out.println("Mac文本编辑器: Open");}@Overridepublic void save() {System.out.println("Mac文本编辑器: Save");}
}public class MacInputMethod implements InputMethod {@Overridepublic void type() {System.out.println("Mac输入法: Type");}@Overridepublic void select() {System.out.println("Mac输入法: Select");}
}

③ 工厂接口

public interface Factory {TextEditor createTextEditor();InputMethod createInputMethod();
}

④ 具体实现工厂
Win平台:

public class WindowsFactory implements Factory{@Overridepublic TextEditor createTextEditor() {return new WindowsTextEditor();}@Overridepublic InputMethod createInputMethod() {return new WindowsInputMethod();}
}

Mac平台:

public class MacFactory implements Factory{@Overridepublic TextEditor createTextEditor() {return new MacTextEditor();}@Overridepublic InputMethod createInputMethod() {return new MacInputMethod();}
}

⑤ 客户端

public class Client {public static void main(String[] args) {// 根据当前操作平台选择工厂类Factory factory;String os = System.getProperty("os.name").toLowerCase();if (os.contains("windows")) {factory = new WindowsFactory();} else if (os.contains("mac")) {factory = new MacFactory();} else {throw new RuntimeException("不在可支持的系统类型范围内");}// 创建文本编辑器和输入法【客户端感知不到不同系统有不同的输入法】TextEditor textEditor = factory.createTextEditor();InputMethod inputMethod = factory.createInputMethod();// 使用文本编辑器和输入法textEditor.open();inputMethod.type();}
}

在这里插入图片描述
代码及类图如上,对于客户端来说,如果是Win平台就使用WindowsFactory来获取编辑器和输入法,如果是Mac平台就使用MacFactory来获取编辑器和输入法。客户端不会感知到编辑器和输入法之间的约束关系,直接使用即可。
在代码可扩展性方面,如果后续增加Linux平台产品族,仅需要增加对应的LinuxTesxEditor、LinuxInputMethod具体产品类以及对应的抽象工厂-LinuxFactory即可,不影响原有业务逻辑,符合开闭原则。【说明】在一些参考资料中认为虽然产品族的扩展很简单,但是产品族内对象种类发生增加需要所有产品族以及工厂改动,因此不符合开闭原则。个人认为不应该这么理解,首先对于开闭原则,之前说过其核心是不改变原有逻辑即是扩展,改变原有逻辑即是改变(应当对其关闭)。因此增加方法、增加属性不改变原有逻辑也并不定算是破坏了开闭原则。再者,如果增加属性方法可能会破坏了接口隔离原则,这时就要思考要不要增加产品族的对象,符合业务逻辑那就应该加入,并且上游也应当参与修改(产品本身已发生改变)。不符合业务逻辑是否应该属于另外一套产品族呢,另外一套产品族是能满足开闭原则的。
抽象工厂模式优点:

  • 满足具有约束关系的产品族对象的创建,进一步避免客户端与产品耦合性
  • 满足开闭原则

抽象工厂模式缺点:

  • 代码逻辑复杂,且类会出现激增。

抽象工厂模式不是用于解决工厂类泛滥问题的方案

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

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

相关文章

使用 ChatGPT 碰到的坑

最近在使用 ChatGPT 的时候碰到一个小坑,因为某些特殊情况我需要使用 syslog 向 logbeat 中发送日志。 由于这是一个比较古老的协议,确实也没接触过,所以就想着让 ChatGPT 帮我生成个例子。 原本我已经在 Go 中将这个流程跑通,所…

PyTorch从零开始实现Transformer

文章目录 自注意力Transformer块编码器解码器块解码器整个Transformer参考来源全部代码(可直接运行) 自注意力 计算公式 代码实现 class SelfAttention(nn.Module):def __init__(self, embed_size, heads):super(SelfAttention, self).__init__()self.e…

ElasticSearch学习--RestClient及案例

目录 RestClient查询文档 快速入门 总结 全文检索(match)查询 精确查询 复合查询 查询总结 排序,分页 高亮 RestClient查询文档 快速入门 总结 全文检索(match)查询 多种查询的差异都在做类型和条件上&#x…

数据可视化——如何绘制地图

文章目录 前言如何绘制地图添加配置项 根据已有数据绘制地图整体代码展示 前言 前面我们学习了如何利用提供的数据来对数据进行处理,然后以折线图的形式展现出来,那么今天我将为大家分享如何将提数据以地图的形式展现。 如何绘制地图 前面我们绘制折线…

如何从gitee上下载项目并把它在本地运行起来

有时候我们会想到在gitee上下载下来项目,那么怎么把项目下载到本地并跑起来呢? 第一步:在git上找到你想要克隆下来的项目,按照如下操作复制项目地址连接,如下图: 以上可以选择HTTPS和SSH两种形式。 第二步…

REST和RPC的区别

1 REST REST 不是一种协议,它是一种架构。大部分REST的实现中使用了RPC的机制,大致由三部分组成: method:动词(GET、POST、PUT、DELETE之类的)Host:URI(统一资源标识)&…

jmeter压测过程中,ServerAgent响应异常:Cannot send data to network connection

ServerAgent异常信息: Cannot send data to network connection(无法将数据发送到网络连接) 原因: linux 防火墙 拦截了当前端口 解决方案: Linux 执行以下命令 /sbin/iptables -I INPUT -p tcp --dport 4445 -j ACC…

数学建模入门-如何从0开始,掌握数学建模的基本技能

一、前言 本文主要面向没有了解过数学建模的同学,帮助同学们如何快速地进行数学建模的入门并且尽快地在各类赛事中获奖,或者写出优秀的数学建模论文。 在本文中,我将从什么是数学建模、数学建模的应用领域、数学建模的基本步骤、数学建模的技…

【动手学深度学习】--12.深度卷积神经网络AlexNet

文章目录 深度卷积神经网络AlexNet1.AlexNet2.模型设计3.激活函数4.模型实现5.读取数据集6.训练AlexNet 深度卷积神经网络AlexNet 学习视频:深度卷积神经网络 AlexNet【动手学深度学习v2】 官方笔记:深度卷积神经网络(AlexNet) …

Android 中 app freezer 原理详解(一):R 版本

基于版本:Android R 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理,为什么叫这个名字,而不…

【Linux | Shell】结构化命令2 - test命令、方括号测试条件、case命令

目录 一、概述二、test 命令2.1 test 命令2.2 方括号测试条件2.3 test 命令和测试条件可以判断的 3 类条件2.3.1 数值比较2.3.2 字符串比较 三、复合条件测试四、if-then 的高级特性五、case 命令 一、概述 上篇文章介绍了 if 语句相关知识。但 if 语句只能执行命令&#xff0c…

Docker 的数据管理、容器互联、镜像创建

目录 一、数据管理 1.数据卷 2. 数据卷容器 二、容器互联(使用centos镜像) 三、Docker 镜像的创建 1.基于现有镜像创建 1.1首先启动一个镜像,在容器里修改 1.2将修改后的容器提交为新的镜像,需使用该容器的id号创建新镜像 …

JAVA SE -- 第十天

(全部来自“韩顺平教育”) 一、枚举(enumeration,简写enum) 枚举是一组常量的集合 1、实现方式 a.自定义类实现枚举 b.使用enum关键字实现枚举 二、自定义类实现枚举 1、注意事项 ①不需要提供setXxx方法&#xff…

HTTP、HTTPS协议详解

文章目录 HTTP是什么报文结构请求头部响应头部 工作原理用户点击一个URL链接后,浏览器和web服务器会执行什么http的版本持久连接和非持久连接无状态与有状态Cookie和Sessionhttp方法:get和post的区别 状态码 HTTPS是什么ssl如何搞到证书nginx中的部署 加…

【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 🥰欢迎并且感谢大家指出小吉的问题 文章目录 🍔概述🍔索引结构⭐B-Tree多路平衡查找树🏳️‍&a…

【iOS】weak关键字的实现原理

前言 关于什么是weak关键字可以去看看我以前的一篇博客:【OC】 属性关键字 weak原理 1. SideTable SideTable 这个结构体,前辈给它总结了一个很形象的名字叫引用计数和弱引用依赖表,因为它主要用于管理对象的引用计数和 weak 表。在 NSOb…

Vite + Vue3 + Ts 【免key、免账号实战本地运行GPT】

🐔 前期回顾 Vue3 Ts Vite —— 封装庆祝彩屑纷飞 示例_彩色之外的博客-CSDN博客封装 彩屑纷飞 示例https://blog.csdn.net/m0_57904695/article/details/131718019?spm1001.2014.3001.5501 目录 🌍 公网 🛹 本地 🪂 源码 &…

【前端|CSS系列第4篇】CSS布局之网格布局

前言 最近在做的一个项目前台首页有一个展示词条的功能,每一个词条都以一个固定大小的词条卡片进行展示,要将所有的词条卡片展示出来,大概是下面这种布局 每一行的卡片数目会随着屏幕大小自动变化,并且希望整个卡片区域周围不要…

20230721 Essex UK, Dongbing Gu 公开讲座--机器人前沿

个人主页: https://www.essex.ac.uk/people/GUDON81301/dongbing-gu 机器人领域任务的特点:dull, dirty, dangerous tasks in remote spaces 机器鱼: 实时港口环境监测 机器鱼群探索算法 化学传感器 水面声呐定位系统/SLAM/通信问题 Robotic …

C—数据的储存(下)

文章目录 前言🌟一、练习一下🌏1.例一🌏2.例二🌏3.例三🌏4.例四 🌟二、浮点型在内存中的储存🌏1.浮点数🌏2.浮点数存储💫(1).二进制浮点数&#x…