瑞_23种设计模式_抽象工厂模式

文章目录

    • 1 抽象工厂模式(Abstract Factory Pattern)
      • 1.1 概念
      • 1.2 介绍
      • 1.3 小结
      • 1.4 结构
    • 2 案例一
      • 2.1 案例需求
      • 2.2 代码实现
    • 3 案例二
      • 3.1 需求
      • 3.2 实现
    • 4 总结
      • 4.1 抽象工厂模式优缺点
      • 4.2 抽象工厂模式使用场景
      • 4.3 抽象工厂模式VS工厂方法模式
      • 4.4 抽象工厂模式VS建造者模式

🙊 前言:本文章为瑞_系列专栏之《23种设计模式》的抽象工厂模式篇。本文中的部分图和概念等资料,来源于博主学习设计模式的相关网站《菜鸟教程 | 设计模式》和《黑马程序员Java设计模式详解》,特此注明。本文中涉及到的软件设计模式的概念、背景、优点、分类、以及UML图的基本知识和设计模式的6大法则等知识,建议阅读 《瑞_23种设计模式_概述》

本系列-设计模式-链接:《瑞_23种设计模式_概述》
本系列-单例模式-链接:《瑞_23种设计模式_单例模式》
本系列-工厂模式-链接:《瑞_23种设计模式_工厂模式》
本系列-原型模式-链接:《瑞_23种设计模式_原型模式》

在这里插入图片描述

1 抽象工厂模式(Abstract Factory Pattern)

1.1 概念

  抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

  抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定具体实现类。通过使用抽象工厂模式,可以将客户端与具体产品的创建过程解耦,使得客户端可以通过工厂接口来创建一族产品。

  抽象工厂模式与工厂方法模式不同,工厂方法模式中考虑的是一类产品的生产,这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

1.2 介绍

  • 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

  • 主要解决:主要解决接口选择的问题。

  • 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

  • 如何解决:在一个产品族里面,定义多个产品。

  • 关键代码:在一个工厂里聚合多个同类产品。

  • 应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。

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

  • 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

  • 使用场景
      1️⃣ QQ 换皮肤,一整套一起换。
      2️⃣ 生成不同操作系统的程序。

  • 注意事项:产品族难扩展,产品等级易扩展。

  抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。客户端通过使用抽象工厂接口来创建产品对象,而不需要直接使用具体产品的实现类。

1.3 小结

  是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

  抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品

  本文要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。

在这里插入图片描述

1.4 结构

  抽象工厂模式的主要角色如下:
    1️⃣ 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
    2️⃣ 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
    3️⃣ 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
    4️⃣ 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

2 案例一

2.1 案例需求

  现有一家咖啡店,要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。

  这个案例使用抽象工厂模式实现的类图如下:

在这里插入图片描述

2.2 代码实现

抽象工厂(接口)
/*** 抽象工厂** @author LiaoYuXing-Ray**/
public interface DessertFactory {// 生产咖啡的功能Coffee createCoffee();// 生产甜品的功能Dessert createDessert();
}
咖啡(抽象类)
/*** 咖啡抽象类** @author LiaoYuXing-Ray**/
public abstract class Coffee {public abstract String getName();// 加糖public void addSugar() {System.out.println("加糖");}// 加奶public void addMilk() {System.out.println("加奶");}
}
甜品(抽象类)
/*** 甜品抽象类** @author LiaoYuXing-Ray**/
public abstract class Dessert {public abstract void show();
}
美式风味的甜品工厂(类)
/*** 美式风味的甜品工厂* - 生产美式咖啡和抹茶慕斯** @author LiaoYuXing-Ray**/
public class AmericanDessertFactory implements DessertFactory {public Coffee createCoffee() {return new AmericanCoffee();}public Dessert createDessert() {return new MatchaMousse();}
}
意大利风味甜品工厂(类)
/*** 意大利风味甜品工厂* - 生产拿铁咖啡和提拉米苏甜品** @author LiaoYuXing-Ray**/
public class ItalyDessertFactory implements DessertFactory {public Coffee createCoffee() {return new LatteCoffee();}public Dessert createDessert() {return new Tiramisu();}
}
美式咖啡(类)
/*** 美式咖啡** @author LiaoYuXing-Ray**/
public class AmericanCoffee extends Coffee {public String getName() {return "美式咖啡";}
}
拿铁咖啡(类)
/*** 拿铁咖啡** @author LiaoYuXing-Ray**/
public class LatteCoffee extends Coffee {public String getName() {return "拿铁咖啡";}
}
抹茶慕斯(类)
/*** 抹茶慕斯类** @author LiaoYuXing-Ray**/
public class MatchaMousse extends Dessert {@Overridepublic void show() {System.out.println("抹茶慕斯");}
}
提拉米苏(类)
/*** 提拉米苏类** @author LiaoYuXing-Ray**/
public class Tiramisu extends Dessert {@Overridepublic void show() {System.out.println("提拉米苏");}
}
测试类
/*** 测试类** @author LiaoYuXing-Ray**/
public class Client {public static void main(String[] args) {// 创建的是意大利风味甜品工厂对象
//         ItalyDessertFactory factory = new ItalyDessertFactory();AmericanDessertFactory factory = new AmericanDessertFactory();// 获取拿铁咖啡和提拉米苏甜品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName());dessert.show();}
}

瑞:该案例实现使用抽象工厂模式,如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

3 案例二

本案例为菜鸟教程中的案例

3.1 需求

  我们将创建 Shape 和 Color 接口和实现这些接口的实体类。下一步是创建抽象工厂类 AbstractFactory。接着定义工厂类 ShapeFactory 和 ColorFactory,这两个工厂类都是扩展了 AbstractFactory。然后创建一个工厂创造器/生成器类 FactoryProducer。

  AbstractFactoryPatternDemo 类使用 FactoryProducer 来获取 AbstractFactory 对象。它将向 AbstractFactory 传递形状信息 Shape(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。同时它还向 AbstractFactory 传递颜色信息 Color(RED / GREEN / BLUE),以便获取它所需对象的类型。

  类图如下:

在这里插入图片描述

3.2 实现

步骤1
  为形状创建一个接口。

Shape.java
public interface Shape {void draw();
}

步骤2
  创建实现接口的实体类。

Rectangle.java
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Inside Rectangle::draw() method.");}
}
Square.java
public class Square implements Shape {@Overridepublic void draw() {System.out.println("Inside Square::draw() method.");}
}
Circle.java
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Inside Circle::draw() method.");}
}

步骤3
  为颜色创建一个接口。

Color.java
public interface Color {void fill();
}

步骤4

  创建实现接口的实体类。

Red.java
public class Red implements Color {@Overridepublic void fill() {System.out.println("Inside Red::fill() method.");}
}
Green.java
public class Green implements Color {@Overridepublic void fill() {System.out.println("Inside Green::fill() method.");}
}
Blue.java
public class Blue implements Color {@Overridepublic void fill() {System.out.println("Inside Blue::fill() method.");}
}

步骤5
  为 Color 和 Shape 对象创建抽象类来获取工厂。

AbstractFactory.java
public abstract class AbstractFactory {public abstract Color getColor(String color);public abstract Shape getShape(String shape);
}

步骤6
  创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。

ShapeFactory.java
public class ShapeFactory extends AbstractFactory {@Overridepublic Shape getShape(String shapeType){if(shapeType == null){return null;}        if(shapeType.equalsIgnoreCase("CIRCLE")){return new Circle();} else if(shapeType.equalsIgnoreCase("RECTANGLE")){return new Rectangle();} else if(shapeType.equalsIgnoreCase("SQUARE")){return new Square();}return null;}@Overridepublic Color getColor(String color) {return null;}
}
ColorFactory.java
public class ColorFactory extends AbstractFactory {@Overridepublic Shape getShape(String shapeType){return null;}@Overridepublic Color getColor(String color) {if(color == null){return null;}        if(color.equalsIgnoreCase("RED")){return new Red();} else if(color.equalsIgnoreCase("GREEN")){return new Green();} else if(color.equalsIgnoreCase("BLUE")){return new Blue();}return null;}
}

步骤7
  创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。

FactoryProducer.java
public class FactoryProducer {public static AbstractFactory getFactory(String choice){if(choice.equalsIgnoreCase("SHAPE")){return new ShapeFactory();} else if(choice.equalsIgnoreCase("COLOR")){return new ColorFactory();}return null;}
}

步骤8
  使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象。

AbstractFactoryPatternDemo.java
public class AbstractFactoryPatternDemo {public static void main(String[] args) {//获取形状工厂AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");//获取形状为 Circle 的对象Shape shape1 = shapeFactory.getShape("CIRCLE");//调用 Circle 的 draw 方法shape1.draw();//获取形状为 Rectangle 的对象Shape shape2 = shapeFactory.getShape("RECTANGLE");//调用 Rectangle 的 draw 方法shape2.draw();//获取形状为 Square 的对象Shape shape3 = shapeFactory.getShape("SQUARE");//调用 Square 的 draw 方法shape3.draw();//获取颜色工厂AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");//获取颜色为 Red 的对象Color color1 = colorFactory.getColor("RED");//调用 Red 的 fill 方法color1.fill();//获取颜色为 Green 的对象Color color2 = colorFactory.getColor("GREEN");//调用 Green 的 fill 方法color2.fill();//获取颜色为 Blue 的对象Color color3 = colorFactory.getColor("BLUE");//调用 Blue 的 fill 方法color3.fill();}
}

步骤9
  执行程序,输出结果:

	Inside Circle::draw() method.Inside Rectangle::draw() method.Inside Square::draw() method.Inside Red::fill() method.Inside Green::fill() method.Inside Blue::fill() method.

4 总结

4.1 抽象工厂模式优缺点

  • 优点

  1️⃣ 解耦:抽象工厂模式有助于降低客户端与具体实现之间的耦合度,从而提高系统的可扩展性和可维护性。
  2️⃣ 灵活性:抽象工厂模式使得系统更加灵活,可以轻松地添加或删除产品族,而无需修改客户端代码。
  3️⃣ 易于测试:由于具体实现与客户端代码的解耦,使得测试变得更加容易,可以更容易地模拟和验证各种不同的工厂实现。
  4️⃣ 多等级产品:抽象工厂模式可以支持多等级的产品,每个等级可以有自己的工厂实现,从而实现更细粒度的控制和配置。

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

  • 缺点

  1️⃣ 代码复杂度增加:使用抽象工厂模式需要编写更多的代码和接口,这可能会增加代码的复杂度和开发时间。
  2️⃣ 违反开闭原则:在抽象工厂模式中,如果要添加一个新的产品族,需要修改抽象工厂接口及其所有子类,这违反了开闭原则(即软件实体应该对扩展开放,对修改封闭)。
  3️⃣ 过度抽象:抽象工厂模式可能会导致过度抽象,使得系统变得更加复杂和难以理解。
  4️⃣ 具体实现依赖:如果具体实现依赖于抽象工厂,那么它们之间的耦合度仍然很高,这可能会限制系统的灵活性和可维护性。

  当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改

4.2 抽象工厂模式使用场景

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

  如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。

4.3 抽象工厂模式VS工厂方法模式

  抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

4.4 抽象工厂模式VS建造者模式

  抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
  建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
  如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。




本文是博主的粗浅理解,可能存在一些错误或不完善之处,如有遗漏或错误欢迎各位补充,谢谢

  如果觉得这篇文章对您有所帮助的话,请动动小手点波关注💗,你的点赞👍收藏⭐️转发🔗评论📝都是对博主最好的支持~


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

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

相关文章

jmeter-03界面介绍

文章目录 主界面介绍工具栏介绍测试计划介绍线程组介绍线程组——选择测试计划,右键-->添加-->线程-->线程组1.线程数2.准备时长(Ramp-up)3.循环次数4.same user on each iteratio5.调度器 主界面介绍 工具栏介绍 新建测试计划:创建一个空白的测…

RNN(神经网络)

目录 介绍: 数据: 模型: 预测: 介绍: RNN,全称为循环神经网络(Recurrent Neural Network),是一种深度学习模型,它主要用于处理和分析序列数据。与传统…

HiveSQL题——collect_set()/collect_list()聚合函数

一、collect_set() /collect_list()介绍 collect_set()函数与collect_list()函数属于高级聚合函数(行转列),将分组中的某列转换成一个数组返回,常与concat_ws()函数连用实现字段拼接效果。 collect_list:收集并形成lis…

GPT3.5\GPT4系列计算完整prompt token数的官方方法

前言: ChatGPT如何计算token数?https://wtl4it.blog.csdn.net/article/details/135116493?spm1001.2014.3001.5502https://wtl4it.blog.csdn.net/article/details/135116493?spm1001.2014.3001.5502 GPT3.5\GPT4系列计算完整prompt token数的官方方法&#xff1…

集成阿里云短信服务

目的是集成阿里云短信服务,完成验证码的发送和接收。 目 录 1、开通阿里云短信服务 2、申请签名 3、申请模板 4、获取AccessKey 5、代码实现 6、代码扩展 7、总结 1、开通阿里云短信服务 去阿里云官网开通 2、申请签名 进行整个步骤时,可以先…

XUbuntu22.04之如何创建、切换多个工作区(二百零九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

项目02《游戏-05-开发》Unity3D

基于 项目02《游戏-04-开发》Unity3D , 【任务】UI背包系统, 首先将Game窗口设置成1920 * 1080, 设置Canvas的缩放模式,:这样设置能让窗口在任意分辨率下都以一个正确的方式显示, 设置数值&…

Grafana实现在同一个面板中多个query应用不同的Data source

目的:实现在同一个面板中多个query应用不同的Data source如下图: 目前A、B都应用的是prometheus源,实现A、B两个query可以应用不同的Data source源 1.添加多个源: 2.点击需要应用多个源的面板,选择上面的设置按钮。 …

2024美赛C题完整解题教程及代码 网球运动的势头

2024 MCM Problem C: Momentum in Tennis (网球运动的势头) 注:在网球运动中,"势头"通常指的是比赛中因一系列事件(如连续得分)而形成的动力或趋势,这可能对比赛结果产生重要影响。球…

【Android】RxJava系列01-基本概述和基本用法

少年啊,要永远相信美好的事情即将发生 【Android】RxJava系列01-基本概述和基本用法 1.RxJava的概述2.RxJava的作用3.观察者和被观察者4.背压5.RxJava的基本用法步骤一,创建Observer(观察者)步骤二,创建Observable&…

【C++】类和对象之运算符重载(三)

前言:在前面我们知道在类和对象中有六个默认成员函数,并学习了其中三个构造函数、析构函数、拷贝构造函数,今天我们将进一步的学习.赋值运算符重载。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质…

【API接口】制造企业,电商API接口面临的一道难题——数据采集

数据采集一直是困扰着所有制造工厂的传统痛点,自动化设备品牌类型繁多,厂家和数据接口各异,国外厂家本地支持有限,不同采购年代。即便产量停机数据自动采集了,也不等于整个制造过程数据都获得了,只要还有其…