抽象工厂模式-Abstract Factory Pattern

2023-11-11

抽象工厂模式-Abstract Factory Pattern

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式

1.先看个不适用于普通工厂方法模式的例子:

开发一套界面皮肤库,可以对Java桌面软件进行界面美化。为了保护版权,该皮肤库源代码不打算公开,而只向用户提供已打包为jar文件的class字节码文件。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,其结构示意图如图所示:

在这里插入图片描述

使用工厂方法模式时初始结构如下:

在这里插入图片描述
但是此结构存在以下问题:

(1) 当需要增加新的皮肤时,虽然不要修改现有代码,但是需要增加大量类,针对每一个新增具体组件都需要增加一个具体工厂,类的个数成对增加,这无疑会导致系统越来越庞大,增加系统的维护成本和运行开销;

(2) 由于同一种风格的具体界面组件通常要一起显示,因此需要为每个组件都选择一个具体工厂,用户在使用时必须逐个进行设置,如果某个具体工厂选择失误将会导致界面显示混乱,虽然我们可以适当增加一些约束语句,但客户端代码和配置文件都较为复杂。

抽象工厂针对此问题就迎刃而解

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有
唯一性,一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候我们希望一个
工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂,它可以生产电视机、电冰
箱、空调等多种电器,而不是只生产某一种电器。为了更好地理解抽象工厂模式,我们先引入两个概
念:

(1) 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

(2) 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

产品等级结构与产品族示意图如图所示:

在这里插入图片描述

在图中,不同颜色的多个正方形、圆形和椭圆形分别构成了三个不同的产品等级结构,而相同颜色的正方形、圆形和椭圆形构成了一个产品族,每一个形状对象都位于某个产品族,并属于某个产品等级结构。图3中一共有五个产品族,分属于三个不同的产品等级结构。我们只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一确定这个产品.

当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。抽象工厂模式示意图如图所示:

在这里插入图片描述
2.抽象工厂模式概述

抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。抽象工厂模式定义如下:

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。

在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图所示:

在这里插入图片描述
抽象工厂模式包含如下角色:

● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。

● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:

abstract class AbstractFactory {
   public abstract AbstractProductA createProductA(); //工厂方法一
   public abstract AbstractProductB createProductB(); //工厂方法二
   ……
   }

具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。对于每一个具体工厂类,其典型代码如下所示:

class ConcreteFactory1 extends AbstractFactory {
   //工厂方法一
   public AbstractProductA createProductA() {
   return new ConcreteProductA1();
   }
  
   //工厂方法二
   public AbstractProductB createProductB() {
   return new ConcreteProductB1();
    }
   
    ……
    }

3.完整解决方案

回到一开始抛出的问题

重构界面皮肤库的设计,其基本结构如图所示:

在这里插入图片描述
在图中,SkinFactory接口充当抽象工厂,其子类SpringSkinFactory和SummerSkinFactory充当具体工厂,接口Button、TextField和ComboBox充当抽象产品,其子类SpringButton、SpringTextField、SpringComboBox和SummerButton、SummerTextField、SummerComboBox充当具体产品。完整代码如下所示:

//在本实例中我们对代码进行了大量简化,实际使用时,界面组件的初始化代码较为复杂,还需要使用JDK中一些已有类,为了突出核心代码,在此只提供框架代码和演示输出。
   //按钮接口:抽象产品
   interface Button {
   public void display();
   }
     //Spring按钮类:具体产品
   class SpringButton implements Button {
   public void display() {
    System.out.println("显示浅绿色按钮。");
    }
    }
   
    //Summer按钮类:具体产品
    class SummerButton implements Button {
    public void display() {
    System.out.println("显示浅蓝色按钮。");
    }
    }
     //文本框接口:抽象产品
    interface TextField {
    public void display();
    }
   
    //Spring文本框类:具体产品
    class SpringTextField implements TextField {
    public void display() {
    System.out.println("显示绿色边框文本框。");
    }
    }
   
    //Summer文本框类:具体产品
    class SummerTextField implements TextField {
    public void display() {
    System.out.println("显示蓝色边框文本框。");
    }
    }
   
    //组合框接口:抽象产品
    interface ComboBox {
    public void display();
    }
   
    //Spring组合框类:具体产品
    class SpringComboBox implements ComboBox {
    public void display() {
    System.out.println("显示绿色边框组合框。");
    }
    }
   
    //Summer组合框类:具体产品
    class SummerComboBox implements ComboBox {
    public void display() {ystem.out.println("显示蓝色边框组合框。");
   }
   }
   
    //界面皮肤工厂接口:抽象工厂
    interface SkinFactory {
    public Button createButton();
    public TextField createTextField();
    public ComboBox createComboBox();
    }
   
    //Spring皮肤工厂:具体工厂
    class SpringSkinFactory implements SkinFactory {
    public Button createButton() {
    return new SpringButton();
    }
   
    public TextField createTextField() {
    return new SpringTextField();
    }
   
    public ComboBox createComboBox() {
    return new SpringComboBox();
    }
    }
   
    //Summer皮肤工厂:具体工厂
    class SummerSkinFactory implements SkinFactory {
    public Button createButton() {
    return new SummerButton();
    }
   
    public TextField createTextField() {
    return new SummerTextField();
    }
   
    public ComboBox createComboBox() {
    return new SummerComboBox();
    }
    }

引入xml配置:

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 {
   //创建文档对象
   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;
    }
    }
    }

xml文件配置:

<?xml version="1.0"?>
   <config>
   <className>SpringSkinFactory</className>
   </config>

客户端代码:

class Client {
   public static void main(String args[]) {
   //使用抽象层定义
    SkinFactory factory;
    Button bt;
    TextField tf;
    ComboBox cb;
    factory = (SkinFactory)XMLUtil.getBean();
    bt = factory.createButton();
    tf = factory.createTextField();
    cb = factory.createComboBox();
    bt.display();
    tf.display();
    cb.display();
    }
    }

4.抽象工厂模式总结

(1)主要优点:

(1) 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

(2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。

(3) 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

主要缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

在以下情况下可以考虑使用抽象工厂模式:

(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。

(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。

(3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。

(4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

抽象工厂模式-Abstract Factory Pattern 的相关文章

  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 如何为俚语和表情符号构建正则表达式 (regex)

    我需要构建一个正则表达式来匹配俚语 即 lol lmao imo 等 和表情符号 即 P 等 我按照以下示例进行操作http www coderanch com t 497238 java java Regular Expression D
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • 攻防世界MISC刷题1-50

    目录 1 ext3 2 base64stego 3 功夫再高也怕菜刀 4 easycap 5 reverseMe 6 Hear with your Eyes 7 What is this 8 normal png 9 something i
  • idea 添加 VUE 的语法支持和开发

    一 VUE的开发分两种 一种是直接在HTML文件中使用 一种是VUE文件的形式开发 1 首先我们先让 HTML 文件支持 VUE 的语法指令提示 2 File gt Setting gt Edit gt Inspections gt htm
  • 父类A a = new 子类B

    父类名 a new 子类名 子类名 b new 子类名 比较上面两种创建实例的区别 a只能调用父类的函数 和子类重写父类的方法 不能调用父类中不存在的子类的函数 因为它没有继承 a是父类的引用 指向了一个子类对象 好处如果一旦发现该B对象无
  • Jetson Orin NX install Fastdeploy

    FastDeploy jetson md at develop PaddlePaddle FastDeploy GitHub sudo apt get install gcc sudo apt get install cmake git c
  • postman-token的作用

    Postman生成的代码中的postman token是什么 What is the postman token in generated code from Postman 这主要用于绕过Chrome 等其他浏览器 中的错误 如果XMLH
  • QEMU/KVM PCI Passthrough(i350) & DPDK 网络性能测试

    QEMU KVM PCI Passthrough i350 DPDK 网络性能测试 硬件要求 CPU必须支持硬件虚拟化 Intel VT d or AMD Vi 和 IOMMU 原图链接 主机配置 设置iommu IOMMU kernel
  • kmp算法(最简单最直观的理解,看完包会)

    本文将以特殊的方式来让人们更好地理解kmp算法 不包括kmp算法的推导 接下来 我们将从朴素算法出发 在这之前 我们先设主串为S 模式串为T 我们要解决的询问是主串中是否包含模式串 即T是否为S的子串 版权声明 本文为原创文章 转载请标明出
  • c++ 继承 学习总结1 继承的基本语法

    前言 继承的作用是减少程序中重复的代码段 如果程序中有很多重复的代码段 可以考虑一下能否使用继承 继承的语法 class 子类 继承方式 父类 include
  • 特征提取-特征工程

    目录 1 定义 2 字典特征提取 3 英文 本特征提取 4 中文 本特征提取 1 定义 将任意数据 如 本或图像 转换为可 于机器学习的数字特征 2 字典特征提取 from sklearn feature extraction import
  • 【算法】树状数组维护总结

    本文仅对树状数组的使用作一个总结 并非讲解 这里的操作都对长度为 n n n 的数组 a a a 进行操作 单点修改 区间查询 暴力做法 修改
  • java使用原始套接字技术进行数据包截获_Linux零拷贝技术,看完这篇文章就懂了...

    本文讲解 Linux 的零拷贝技术 云计算是一门很庞大的技术学科 融合了很多技术 Linux 算是比较基础的技术 所以 学好 Linux 对于云计算的学习会有比较大的帮助 本文借鉴并总结了几种比较常见的 Linux 下的零拷贝技术 相关的引
  • python的pyecharts绘制各种图表详细(代码)

    环境 pyecharts库 echarts countries pypkg echarts china provinces pypkg echarts china cities pypkg 数据 2018年4月16号的全国各地最高最低和天气
  • 5.5js

    1 JavaScript简介 什么是JavaScript JavaScript 是 种客户端脚本语 脚本语 是 种轻量级的编程语 JavaScript 通常被直接嵌 HTML 由浏览器解释执 JavaScript 是 种解释性语 就是说 代
  • Deepin 手动分区记录

    起初安装Deepin 采用手动分区 总是安装失败 经过以下分区就成功安装了 efi 分区 默认300m boot 分区 默认 512m 交换分区 swap 等于你的内存大小 分区 15G home 分区剩余全部容量 home可以设置也可以不
  • brpc组件bvar源码解析(三)Variable、Reducer和Adder

    1 Variable类 Variable是所有bvar的基类 是一个纯虚类 拥有的唯一的成员变量是 name Variable类中的接口分为几类 描述相关的 子类实现纯虚函数describe 目的是将bvar的值写入ostream get
  • 验证码倒计时

    获取验证码倒计时 return second 120 getCodeFn let flag true if this user phone this http isPhone this user phone false this http
  • 斐讯 K2 路由器 无线中继 无线扩展设置教程图文

    斐讯 K2 路由器无线扩展设置教程 1 连接上k2路由器无线网络 2 登录k2路由器管理页面 192 168 2 1 3 上网设置 4 无线设置 5 设置k2无线扩展功能 6 选择主路由器无线网络 7 设置无线网络信息wifi名称 wifi
  • 详解redis的哨兵模式(1)

    目录 1 背景 2 实现过程 2 1 初始化服务器 2 2 将普通Redis服务器使用的代码替换成Sentinel专用代码 2 3初始化Sentinel状态 2 4初始化sentinel状态的masters属性 2 5创建连向主服务器的网络
  • 视频重编码为h265重新封装MP4并截断

    MP4 重编码重封装 分辨率和编码格式 转为h265 调整 XFormat h pragma once include
  • 抽象工厂模式-Abstract Factory Pattern

    抽象工厂模式 Abstract Factory Pattern 抽象工厂模式 Abstract Factory Pattern 提供一个创建一系列相关或相互依赖对象的接口 而无须指定它们具体的类 抽象工厂模式又称为Kit模式 它是一种对象创