【设计模式深度剖析】【3】【创建型】【抽象工厂模式】| 要和【工厂方法模式】对比加深理解

👈️上一篇:工厂方法模式    |   下一篇:建造者模式👉️

目录

  • 抽象工厂模式
    • 前言
    • 概览
    • 定义
      • 英文原话
      • 直译
      • 什么意思呢?(以运动型车族工厂,生产汽车、摩托产品为例)
    • 类图
    • 4个角色
      • 抽象工厂(Abstract Factory)角色
      • 具体工厂(Concrete Factory)角色
      • 抽象产品(Abstract Product)角色
      • 具体产品(Concrete Product)角色
    • 代码说明
      • 1. 抽象工厂类:AbstractFactory.java 接口类
      • 2. 具体工厂类:
        • 2.1 产品族1的具体工厂类:ConcreteFactoryFamily1.java
        • 2.2 产品族2的具体工厂类:ConcreteFactoryFamily2.java
      • 3. 抽象产品A类:ProductA.java
      • 4. A类产品具体产品类:
        • 4.1 产品族为1的A具体产品类:ProductA1.java
        • 4.2 产品族为2的A具体产品类:ProductA2.java
      • 5. 抽象产品B类:ProductB.java
      • 6. B类产品具体产品类:
        • 6.1 产品族为1的B具体产品类:ProductB1.java
        • 6.2 产品族为2的B具体产品类:ProductB2.java
      • 7. 测试类
    • 应用
      • 优点
      • 缺点
        • 情景思考,加深理解:
      • 使用场景

抽象工厂模式

前言

当一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。(多个产品族1,2,3,产品族4… ,每个产品族都有A、B、C等等产品),

如果产品族中只有一种产品,则抽象工厂模式就退化为工厂方法模式

和上一篇【设计模式深度剖析】【2】【创建型】【工厂方法模式】 对比理解,加深理解

上一篇【设计模式深度剖析】【2】【创建型】【工厂方法模式】中提到:

工厂方法模式只能针对一个产品等级结构如A产品B产品。

但是如果是A产品与B产品的第一等级、第二等级,这种情况要抽象工厂模式

也就是它(抽象工厂模式)解决的问题是:多个产品族,多种产品。

多个产品族,每个产品族下又有多种产品——使用抽象工厂模式;

而,仅仅生产某种产品——使用工厂方法模式。


抽象工厂模式(Abstract Factory Pattern)是一种比较常用的模式

抽象工厂模式是工厂方法模式的升级版本。在有多个业务品种、业务分类时,

通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

======== 本文代码示例 ========

概览

  • 定义
  • 4个角色
  • 应用
    • 优点
    • 缺点
    • 使用场景

定义

英文原话

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

直译

意思是:提供一个接口,用于创建相关或依赖的对象族,而无需指定它们的具体类。

什么意思呢?(以运动型车族工厂,生产汽车、摩托产品为例)

这句话描述了一个设计模式的概念,特别是抽象工厂模式(Abstract Factory Pattern)。抽象工厂模式是一种创建型设计模式,它提供了一种方式来封装一组具有共同主题的单独的工厂接口,而无需指定它们的具体类。

简单来说,假设你有两个或更多的产品族(例如,汽车和摩托车),并且每个产品族中都有多种类型的对象(例如,汽车有跑车、SUV等;摩托车有越野车、街车等)

你可能想要一个系统,该系统能够基于一些配置或条件来生成这些产品,但你不希望直接依赖于具体的实现类

抽象工厂模式允许你定义一个接口(抽象工厂),该接口中包含了创建多个产品的方法(每个方法对应于一个产品族中的一个产品类型)。然后,你可以为每种产品族实现这个抽象工厂接口,从而创建具体的产品对象

以下是一个简化的抽象工厂模式的例子:

// 抽象工厂接口-车辆工厂,能生产两种产品-汽车和摩托  
public interface VehicleFactory {  
    Car createCar();  
    Motorcycle createMotorcycle();  
}  
  
// 抽象产品接口-汽车接口  
public interface Car {  
    // ... Car specific methods  
}  
  
// 抽象产品接口-摩托接口
public interface Motorcycle {  
    // ... Motorcycle specific methods  
}  

  
// 具体的产品类(例如,其中一个产品族-运动型车辆族:运动型车辆和运动型摩托车)  
// ... 省略具体实现  
  
  
// 具体工厂类 - 对应于一个产品族,运动型车辆,可以生产运动型汽车、运动型摩托车  
public class SportsCarFactory implements VehicleFactory {  
    @Override  
    public Car createCar() {  
        return new SportsCar(); // 假设这是具体的运动型汽车  
    }  
  
    @Override  
    public Motorcycle createMotorcycle() {  
        return new SportsMotorcycle(); // 假设这是具体的运动型摩托车  
    }  
}  

// 客户端代码  
public class Client {  
    public static void main(String[] args) {  
        VehicleFactory factory = new SportsCarFactory();  
        Car car = factory.createCar();  
        Motorcycle motorcycle = factory.createMotorcycle();  
        // ... 使用car和motorcycle  
    }  
}

在这个例子中,

VehicleFactory 是一个抽象工厂接口,它定义了两个方法(createCarcreateMotorcycle)来创建产品。

SportsCarFactory 是一个具体工厂类**(用于同一产品族[运动型族]的不同类型产品[汽车、摩托车])**,它实现了 VehicleFactory(抽象工厂) 接口,并提供了具体的产品实现(SportsCarSportsMotorcycle)。

客户端代码使用 SportsCarFactory 来创建和使用 CarMotorcycle 对象,而无需直接依赖于具体的实现类

类图

抽象工厂模式类图(本示例程序uml)

4个角色

4个角色与工厂方法模式类似:

抽象工厂(Abstract Factory)角色

该角色是抽象工厂模式的核心,与应用系统无关,任何创建对象的工厂类必须实现这个接口。

具体工厂(Concrete Factory)角色

该角色实现了抽象工厂接口,含有选择合适的产品对象的逻辑,并且受到应用程序的调用以创建产品对象。

抽象产品(Abstract Product)角色

该角色负责定义产品的共性,实现对产品最抽象的定义。

具体产品(Concrete Product)角色

该角色实现抽象产品角色所声明的接口**,抽象工厂模式所创建的任何产品对象都是某个具体产品角色的实例。

代码说明

代码示例

1. 抽象工厂类:AbstractFactory.java 接口类

package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 抽象工厂接口:声明有两种产品A与B,定义工厂方法,返回产品A与B
 *
 * @author Polaris 2024/5/16
 */
public interface AbstractFactory {
    //创建产品A
    ProductA factoryA();

    //创建产品B
    ProductB factoryB();
}

2. 具体工厂类:

2.1 产品族1的具体工厂类:ConcreteFactoryFamily1.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体工厂类1:实现了抽象工厂接口,生产产品族为1的A产品与B产品。
 *
 * @author Polaris 2024/5/16
 */
public class ConcreteFactoryFamily1 implements AbstractFactory {

    //创建产品族为1的产品A
    @Override
    public ProductA factoryA() {
        return new ProductA1();
    }

    //创建产品族为1的产品B
    @Override
    public ProductB factoryB() {
        return new ProductB1();
    }
}
2.2 产品族2的具体工厂类:ConcreteFactoryFamily2.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体工厂类2:实现了抽象工厂接口,生产产品族为2的A产品与B产品。
 *
 * @author Polaris 2024/5/16
 */
public class ConcreteFactoryFamily2 implements AbstractFactory {
    //创建产品族为2的产品A
    @Override
    public ProductA factoryA() {
        return new ProductA2();
    }

    //创建产品族为2的产品B
    @Override
    public ProductB factoryB() {
        return new ProductB2();
    }
}

3. 抽象产品A类:ProductA.java

package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 抽象产品A:抽象产品接口,声明了产品A的方法名称、返回类型
 *
 * @author Polaris 2024/5/16
 */
public interface ProductA {
    //产品A的公共方法
    void method1();

    void method2();
}

4. A类产品具体产品类:

4.1 产品族为1的A具体产品类:ProductA1.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体产品A1:实现了抽象产品A接口
 * 产品族为1的A产品
 *
 * @author Polaris 2024/5/16
 */
public class ProductA1 implements ProductA {

    @Override
    public void method1() {
        System.out.println("产品族为1的产品A的实现方法");
    }

    @Override
    public void method2() {
        //业务逻辑代码
    }
}
4.2 产品族为2的A具体产品类:ProductA2.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体产品A2:实现了抽象产品A接口
 * 产品族为2的A产品
 *
 * @author Polaris 2024/5/16
 */
public class ProductA2 implements ProductA {

    @Override
    public void method1() {
        System.out.println("等级为2的产品A的实现方法");
    }

    @Override
    public void method2() {
        //业务逻辑代码
    }
}

5. 抽象产品B类:ProductB.java

package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 抽象产品B:抽象产品接口,声明了产品B的方法名称、返回类型
 *
 * @author Polaris 2024/5/16
 */
public interface ProductB {
    //产品B的公共方法
    void method1();

    void method2();
}

6. B类产品具体产品类:

6.1 产品族为1的B具体产品类:ProductB1.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体产品B1:实现了抽象产品B接口
 * 产品族为1的B产品
 *
 * @author Polaris 2024/5/16
 */
public class ProductB1 implements ProductB {

    @Override
    public void method1() {
        System.out.println("等级为1的产品B的实现方法");
    }

    @Override
    public void method2() {
        //业务逻辑代码
    }
}
6.2 产品族为2的B具体产品类:ProductB2.java
package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 具体产品B2:实现了抽象产品B接口
 * 产品族为2的B产品
 *
 * @author Polaris 2024/5/16
 */
public class ProductB2 implements ProductB {

    @Override
    public void method1() {
        System.out.println("等级为2的产品B的实现方法");
    }

    @Override
    public void method2() {
        //业务逻辑代码
    }
}

7. 测试类

  • 抽象工厂类,规定了要生产A产品和B产品(接口就是用来定标准,规范标准的,具体到这里就是规定用来生产A产品和B产品)至于怎么生产,由具体工厂类去实现;

  • 它的引用指向一个具体工厂类-生产产品族为1的具体工厂类的实例;

  • 该产品族工厂-具体工厂,可以生产A和B产品,即1族的A与B产品,即A1, B1产品

  • 生产出的A1, A2实例可以执行业务逻辑,

同理,生产产品族为2的产品A和产品B,即A2, B2,也是一样的。

package com.polaris.designpattern.list1.creational.pattern3.abstractfactory;

/**
 * 测试类
 * @author Polaris 2024/5/16
 */
public class ClientDemo {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactoryFamily1();
        //生产产品族为1的产品A
        ProductA a1 = factory1.factoryA();
        //生产产品族为1的产品B
        ProductB b1 = factory1.factoryB();
        //执行业务逻辑
        a1.method1();
        b1.method1();
        
        
        AbstractFactory factory2 = new ConcreteFactoryFamily2();
        //生产产品族为2的产品A
        ProductA a2 = factory2.factoryA();
        //生产等级为1的产品B
        ProductB b2 = factory2.factoryB();
        //执行业务逻辑
        a2.method1();
        b2.method1();
    }
}

应用

优点

抽象工厂模式工厂方法模式的进一步抽象针对的是一族产品如果产品族中只有一种产品,则抽象工厂模式就退化为工厂方法模式。除了工厂方法模式的优点外,抽象工厂模式还具有下列优点。

  • 产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。
  • 生产线扩展非常容易,如果要针对同一产品族建立新的生产线,只需要 实现 产品族中的所有产品接口建立新的工厂类即可
    • ProductA与ProductB就是产品族(类),即A族产品与B族产品;
    • ProductA1与ProductA2(或ProductB1与ProductB2)就是A族产品(或B族产品)产品族下不同产品线
    • 如A类产品再加一条产品线(族)生产ProductA3(产品族为3的A产品),则需要ProductA3实现ProductA接口方法 ,由于抽象工厂已经定义了A组产品的约束(生产A产品对象的方法),所以对抽象工厂无需改动;但是,每条产品线的产品都需要一个具体工厂类创建对象,因此需要添加一个具体工厂(ConcreteFactoryFamily3)生产新增加的产品线产品A3

缺点

抽象工厂模式的最大缺点就是产品族本身的扩展非常困难,如果需要在产品族中增加一个新的产品类型,则需要修改多个接口并且会影响已有的工厂类

* 抽象工厂声明的方法针对所有产品族产品;
* 新增加一个产品族(C类产品),抽象工厂需要添加生产新产品族产品的接口(声明方法);
* 具体工厂是要实现抽象工厂所有接口的,所以相应的具体工厂(所有各个产品线)需要修改(实现创建新增C产品族产品的方法)
情景思考,加深理解:
  • 比如此示例:两个产品族(即2个种类产品),两条产品线(分别生产1等级与2等级产品)
  • 若:仅仅增加一个产品C(现有产品线都生产这个新增产品类型):
    • 创建产品族C接口,并增加C1/C2产品类实现C类接口
    • 抽象工厂添加创建此类型产品的接口;
    • 所有产品线(具体工厂,某一族)都增加实现接口的方法;
  • 若:现在增加一个产品(即增加1个新产品类型C),但是只生产最高等级(即产品线只有一条(产品线1,产品族1)来生产此产品族产品C),该怎么办?
    • 抽象工厂抽象了各类产品的生产,其他产品线(生产某一族产品)也要实现该族产品生产的接口(方法),但是,不需要创建出相应对象(产品)(因为其余产品线不生产新增产品族产品),可以抛出异常
  • 若:新增产品C,且单独用额外的产品线3生产此族产品(即新产品类型,新开辟产品线专门只用于生产此族产品),该怎么弄?
    • 如上个问题,新增产品族,抽象工厂必定添加接口方法用于生产此产品,现有产品线都要实现这个接口(但是根据需求生产不出来,抛出异常);
    • 新开辟的产品线由于抽象工厂的约束,它要实现生产所有产品族(包括新增产品族)的接口,这条产品线只需要生产新增产品族,生产其他已有产品族产品时抛出异常即可。
  • 所以,新增产品族比较麻烦,涉及到新增产品C接口及具体产品线产品,以及抽象工厂、具体工厂的变动,这是个很大的变动。即:产品族本身的扩展非常困难

使用场景

抽象工厂模式的使用场景是:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。

抽象工厂模式是一个简单的模式,使用的场景非常多,

例如,应用需要涉及不同操作系统的时候,可以考虑使用抽象工厂模式,如果需要在三个不同的平台(Windows、Linux、Android)上运行,则可以通过抽象工厂模式屏蔽掉操作系统对应用的影响。三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似的,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。

👈️上一篇:工厂方法模式    |   下一篇:建造者模式👉️

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

起底震网病毒的来龙去脉

2010年,震网病毒被发现,引起世界哗然,在后续的10年间,陆陆续续有更多关于该病毒的背景和细节曝光。今年,《以色列时报》和《荷兰日报》又披露了关于此事件的更多信息,基于这些信息,我们重新梳理…

使用 Docker 部署 Jenkins 并设置初始管理员密码

使用 Docker 部署 Jenkins 并设置初始管理员密码 每一次开始,我都特别的认真与胆怯,是因为我期待结局,也能够不会那么粗糙,不会让我失望,所以,就多了些思考,多了些拘束,所以&#xf…

软件测试:功能测试-接口测试-自动化测试-性能测试-验收测试

软件测试的主要流程 一、测试主要的四个阶段 1.测试计划设计阶段:产品立项之后,进行需求分析,需求评审,业务需求评级,绘制业务流程图。确定测试负责人,开始制定测试计划; 2.测试准备阶段&…

不小心丢失mfc140u.dll文件怎么办?mfc140u.dll丢失的解决办法

当您发现mfc140u.dll文件不见了或者受损,别担心,我们可以一起解决这个问题!首先,您可能会注意到一个小提示,当您尝试打开某些程序时,屏幕上会跳出一个消息说“找不到mfc140u.dll”或者“mfc140u.dll文件缺失…

心识宇宙 x TapData:如何加速落地实时数仓,助力 AI 企业智慧决策

使用 TapData,化繁为简,摆脱手动搭建、维护数据管道的诸多烦扰,轻量代替 OGG、DSG 等同步工具,「CDC 流处理 数据集成」组合拳,加速仓内数据流转,帮助企业将真正具有业务价值的数据作用到实处&#xff0c…

Python的selenium爬取

1.selenium 1.1.前言 使用python的requests模块还是存在很大的局限性,例如:只发一次请求;针对ajax动态加载的网页则无法获取数据等等问题。特此,本章节将通过selenium模拟浏览器来完成更高级的爬虫抓取任务。 1.2.什么是seleniu…

学习单向链表带哨兵demo

一、定义 在计算机科学中,链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。 1.可以分三类为 单向链表,每个元素只知道其下一个元素是谁 双向链表,每个元素知道其上一个元素和下一个元素 …

抖音小店不能做无货源了吗?当然不是,而是玩法更先进了!

大家好,我是电商糖果 自从2023年抖音小店开始严查无货源,不少商家被平台处罚,被逼无奈退出抖音小店。 网上关于抖音小店不能做无货源的声音越来越多。 可是一年多过去,大家渐渐的发现,平台内还是有很多无货源商家&a…

Sping源码(八)—registerBeanPostProcessors

序言 之前我们用大量的篇幅介绍过invokeBeanFactoryPostProcessors()方法的执行流程。 而invokeBeanFactoryPostProcessors的主要逻辑就是遍历执行实现了BeanDefinitionRegistryPostProcesso类(主要是针对BeanDefinition的操作)和BeanFactoryPostProcessor(主要针对BeanFacrot…

spring-boot集成slf4j(二)logback配置详解

一、configuration 根节点:configuration,作为顶级标签, 可以用来配置一些lockback的全局属性,常见的属性如下: (1)scan“true” :scan是否开启自动扫描,监控配置文件更…

XShell-连接-Centos 7

XShell 连接Centos 7 一.准备 安装XShell XShell下载地址: 在虚拟机上安装Centos 7,具体操作自行学习 二.Centos 7的准备 1.网络适配器修改为NAT 2.获取IP 输入命令: ip addr我的Centos 7对外IP为192.168.174.129 三.XShell连接Cento…

Big Demo Day第十三期活动即将启幕,Web3创新项目精彩纷呈,PEPE大奖等你抽取

5月28号在香港数码港 Big Demo Day第十三期 活动即将拉开帷幕,活动将汇集众多Web3领域的创新项目,为参会者带来一场科技与智慧交融的盛宴。在这里,你不仅能深入了解区块链、AI等前沿技术的最新应用,还能有机会赢取丰厚的PEPE大奖。…

使用maven-helper插件解决jar包冲突

发现问题 maven-helper分析问题 如上所述,问题就是依赖版本冲突了,出现版本冲突的原因是因为由于Maven具有依赖传递性,所以当你引入一个依赖类的同时,其身后的依赖类也一起如过江之鲫纷至沓来了。 举个例子:   A依赖…

保护元件-详实的熔断器(保险丝)知识

目录: 一、汽车保险丝设计与选型 1、概述 2、构造及工作原理 1)构造 2)工作原理 3)保险丝熔断及分断时间 4)时间/电流特性曲线 5)环境温度修正系数 3、熔化热能值I2t★ 4、三种电流模型 1&a…

废物回收机构|基于SprinBoot+vue的地方废物回收机构管理系统(源码+数据库+文档)

地方废物回收机构管理系统 目录 基于SprinBootvue的地方废物回收机构管理系统 一、前言 二、系统设计 三、系统功能设计 1管理员功能模块 2 员工功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍…

瑞芯微RV1126——人脸识别框架分析

项目核心是在Linux平台上利用摄像头采集人脸,并进行人脸识别。这个项目使用的是FFMPEGOPENCV虹软框架完成。 FFMPEG的主要工作是负责采集摄像头的数据并把摄像头数据发送给opencv。 Opencv的主要工作则是把摄像头数据转换成矩阵数据。 虹软的主要功能则是利用Open…

K8s 二进制部署---下篇(多master节点 负载均衡 高可用)

一 master02 节点部署 master01192.168.11.5kube-apiserver,kube-controller-manager,kube-scheduler,etcdmaster02192.168.11.12kube-apiserver,kube-controller-manager,kube-scheduler,etcdnode01192.1…

WebGL渲染引擎优化方向——渲染帧率的优化

作者:caven chen 对此内容感兴趣还可以看前文: WebGL渲染引擎优化方向——加载性能优化 前言 WebGL 是一种强大的图形渲染技术,可以在浏览器中快速渲染复杂的 3D 场景。但是,由于 WebGL 的高性能和高质量要求,如果…

白嫖免费图床!CloudFlare R2太香了!

1 为啥要折腾搭建一个专属图床? 技术大佬写博客都用 md 格式,要在多平台发布,图片就得有外链后续如博客迁移,国内博客网站如掘金,简书,语雀等都做了防盗链,图片无法迁移 2 为啥选择CloudFlare…

记录一个写SpringBoot中Hive数据可以正常提取但无法存到MySQL的bug

【背景】 我正在用SpringBoot框架写一个数据治理项目,目前所处阶段是将hive和hdfs中的元数据提取出来,存储到MySQL中,我的hive和hdfs上的数据存储在三台Linux服务器上(hadoop102-104),MySQL在我本地Window…