Java设计模式 | 抽象工厂模式

在这里插入图片描述

抽象工厂模式

工厂方法模式中考虑的是一类产品的生产,如幼儿园只培养小朋友,鞋厂只生产鞋子。
这些工厂只生产同种类产品,同种类产品称为同等级产品,即工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂都是综合型工厂,可以生产多等级(种类)的产品,如大学既有软件工程专业,也有汉语言文学专业等。
抽象工厂模式将考虑多等级产品的生产,将一个具体工厂所生产的位于不同等级的一系列产品称为一个产品族。如下图,横轴是产品等级,也就是一类产品,纵轴是产品族,表示同一品牌的产品,同一品牌的产品产自同一工厂。
image-20200401214509176.png

是什么

抽象工厂模式就是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。(抽象工厂模式可以确保一系列相关的产品被一起创建,这些产品能够相互配合使用)
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级(种类)的产品。

结构

抽象工厂的主要角色如下:

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

咖啡店业务发生发生变化,不仅要生产咖啡,还要生产甜点,如提拉米苏、抹茶慕斯等。如果按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸的情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏和抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(都属于意大利风味);美式咖啡和抹茶慕斯是同一产品族(都是美式风味)。可以使用抽象工厂模式实现:
实现抽象⼯⼚模式,一般需要遵循以下步骤:

  • 定义抽象产品接⼝(可以有多个),接⼝中声明产品的公共⽅法。
  • 实现具体产品类,在类中实现抽象产品接⼝中的⽅法。
  • 定义抽象⼯⼚接⼝,声明⼀组⽤于创建产品的⽅法。
  • 实现具体⼯⼚类,分别实现抽象⼯⼚接⼝中的⽅法,每个⽅法负责创建⼀组相关的产品。
  • 在客户端中使⽤抽象⼯⼚和抽象产品,⽽不直接使⽤具体产品的类名。

image.png
抽象工厂:

public interface DessertFactory {// 生产咖啡的功能Coffee createCoffee();// 生产甜点的功能Dessert createDessert();
}

具体工厂(美式风味甜品工厂):

public class AmericanDessertFactory implements DessertFactory{@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}@Overridepublic Dessert createDessert() {return new MatchaMousse();}
}

具体工厂(意大利式风味甜品工厂):

public class ItalyDessertFactory implements DessertFactory {@Overridepublic Coffee createCoffee() {return new LatteCoffee();}@Overridepublic Dessert createDessert() {return new Trimisu();}
}

抽象产品(咖啡类):

public abstract class Coffee {// 获取咖啡名public abstract String getName();// 加糖public void addSugar(){System.out.println("加糖");}// 加奶public void addMilk(){System.out.println("加奶");}
}

抽象产品(甜点类):

public abstract class Dessert {public abstract void show();
}

具体产品(美式咖啡类):

public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}

具体产品(拿铁咖啡类):

public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿铁咖啡";}
}

具体产品(抹茶慕斯类):

public class MatchaMousse extends Dessert {@Overridepublic void show() {System.out.println("抹茶慕斯");}
}

具体产品(提拉米苏类):

public class Trimisu extends Dessert {@Overridepublic void show() {System.out.println("提拉米苏");}
}

测试:

public class Client {public static void main(String[] args) {// 创建意大利风味的甜品工厂对象
//        DessertFactory dessertFactory = new ItalyDessertFactory();// 创建美式风味的甜品工厂对象DessertFactory dessertFactory = new AmericanDessertFactory();// 生产对应的咖啡和甜点Coffee coffee = dessertFactory.createCoffee();Dessert dessert = dessertFactory.createDessert();System.out.println(coffee.getName());dessert.show();}
}

总结

抽象工厂模式能够保证一系列相关产品一起使用,且在不改变客户端代码的情况下,可以方便的替换整个产品系列。但是需要增加新产品类时,除了要增加新的具体产品类,还需要修改抽象⼯⼚接⼝及其所有的具体⼯⼚ 类,扩展性相对较差。
优点:

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

缺点:

  • 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
使用场景
  • 当需要创建的对象是一系列相关联或相互依赖的产品族时
  • 系统中有多个产品族,但每次只使用其中的某一产品族
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
简单工厂、工厂方法和抽象工厂的区别
  • 简单⼯⼚模式:⼀个⼯⼚⽅法创建所有具体产品
  • ⼯⼚⽅法模式:⼀个⼯⼚⽅法创建⼀个具体产品
  • 抽象⼯⼚模式:⼀个⼯⼚⽅法可以创建⼀类具体产品

模式扩展

简单工厂+配置文件解耦

可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可
配置文件bean.properties:

american=com.jmd.factory.configfactory.AmericanCoffee
latte=com.jmd.factory.configfactory.LatteCoffee

工厂类:

public class CoffeeFactory {// 加载配置文件,获取配置文件中配置的全类名,并创建对象进行存储// 1 定义容器存储咖啡对象private static HashMap<String, Coffee> coffeeMap = new HashMap<>();// 2 加载配置文件,只需加载一次static {// 2.1 创建Properties对象Properties p = new Properties();// 2.2 加载配置文件load()InputStream rs = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");if (rs == null) {throw new RuntimeException("找不到配置文件");}try {p.load(rs);// 从p集合中获取全类名并创建对象for (Object key : p.keySet()) {// 2.3 获取全类名String className = p.getProperty((String) key);// 通过反射创建对象// 2.4 加载类Class clazz = Class.forName(className);// 2.5 创建对象Coffee coffee = (Coffee) clazz.newInstance();// 2.6 将对象存入容器coffeeMap.put((String) key, coffee);}} catch (Exception e) {e.printStackTrace();}}// 根据名称获取对象public static Coffee createCoffee(String type) {return coffeeMap.get(type);}
}

抽象产品(咖啡类):

public abstract class Coffee {// 获取咖啡名public abstract String getName();// 加糖public void addSugar(){System.out.println("加糖");}// 加奶public void addMilk(){System.out.println("加奶");}
}

具体产品(美式咖啡):

public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}

具体产品(拿铁咖啡):

public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿铁咖啡";}
}

测试:

public class Client {public static void main(String[] args) {Coffee coffee = CoffeeFactory.createCoffee("latte");System.out.println(coffee.getName());System.out.println("===========================");Coffee coffee2 = CoffeeFactory.createCoffee("american");System.out.println(coffee2.getName());}
}

静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次

JDK源码分析

  • Calendar类中的getInstance()方法使用的是简单工厂模式
  • 单列集合迭代器Collection.iterator()方法使用的是工厂方法模式
  • DateForamt类中的getInstance()方法使用的是简单工厂模式

github笔记

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

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

相关文章

有什么代理IP推荐?如何分辨代理IP类型?

跨境外贸是近几年来的热门行业&#xff0c;在众多助力跨境出海的工具中&#xff0c;代理IP也是强力的一大保障。不仅可以帮助企业拓展更大的地区市场&#xff0c;更加顺畅进行市场调查&#xff0c;更重要地&#xff0c;在TikTok、Amazon、Ebay、Instagram、Etsy等等跨境平台业务…

介绍一下Redis的集群模式?

Redis有三种主要的集群模式&#xff0c;用于在分布式环境中实现高可用性和数据复制。这些集群模式分别是&#xff1a;主从复制&#xff08;Master-Slave Replication&#xff09;、哨兵模式&#xff08;Sentinel&#xff09;和Redis Cluster模式。 一、问题解析 主从模式 主从…

学点儿数据库_Day11_多表、等值连接、内连接、模糊查找

1 多表 学生表、班级表、课程表、班级课程表 关系型数据库&#xff1a; MySql、SqlServer、Oracle 相同的数据出现多次绝不是一件好事&#xff0c;这是关系数据库设计的基础。关系表的设计就是要把信息分解成多个表&#xff0c;一个数据一个表&#xff0c;各表通过某些共同的…

OpenCV模块熟悉:点云处理相关

1. 显示--VIZ 曾经基于PCL 做过不少点云相关的开发&#xff0c;采样VTK进行有点云显示。后来基于OpenCV做了不少三维重建工作&#xff0c;总是将点云保存下来&#xff0c;然后借助CloudCompare等查看结果。如果能够将VIZ编译进来&#xff0c;预计会提升开发速度。 …

86.分隔链表

给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1&#xff1a; ​ 输入&#xff1a;head [1,4,3,2,5,2], x 3 输出&…

unity学习(70)——编译游戏发生错误2

1.全屏问题其实无所谓&#xff0c;windows用tab可以切出来的。 2.现在主要问题是服务器try了以后虽然不崩溃了&#xff0c;但不再显示2个实例对象了&#xff0c;unity和exe此时都只能看到一个实例对象 2.1把之前报错位置的try-catch先注释掉 2.2 unity中此时登录666账号&…

Git工具的详细使用

一、环境说明 [rootgit ~]# getenforce Disabled [rootgit ~]# systemctl status firewalld ● firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)Active: inactive (d…

Avalonia笔记2 -数据集合类控件

学习笔记&#xff1a; 1. DataGrid 笔记1中已经记录&#xff1b; 2. ItemsControl 属性&#xff1a; ItemsSource&#xff1a;数据源 ItemsControl.ItemTemplate&#xff1a;单项数据模板&#xff0c;内部使用<DataTemplate> 示例&#xff1a; <ItemsContr…

docker关闭全部运行容器命令是什么?

环境&#xff1a; docker v22.1 问题描述&#xff1a; docker关闭全部运行容器命令是什么&#xff1f; 解决方案&#xff1a; 要关闭所有正在运行的Docker容器&#xff0c;可以使用如下命令&#xff1a; docker stop $(docker ps -a -q)这条命令首先执行 docker ps -a -q…

35.HarmonyOS App(ArkUI)使用父组件@Builder装饰的方法初始化子组件@BuilderParam报错

HarmonyOS App(ArkUI)使用父组件Builder装饰的方法初始化子组件BuilderParam报错 Type void is not assignable to type () > void. <tsCheck> 去掉括号()就可以了 装饰器&#xff1a; 用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。如上述示例中En…

使用LangChain LCEL生成RAG应用、使用LangChain TruLens对抗RAG幻觉

# 导入LangChain的库 from langchain import *# 加载数据源 loader WebBaseLoader() doc loader.load("https://xxx.html")# 分割文档对象 splitter RecursiveCharacterTextSplitter(max_length512) docs splitter.split(doc)# 转换文档对象为嵌入&#xff0c;并…

[ Linux ] git工具的基本使用(仓库的构建,提交)

1.安装git yum install -y git 2.打开Gitee&#xff0c;创建你的远程仓库&#xff0c;根据提示初始化本地仓库&#xff08;这里以我的仓库为例&#xff09; 新建好仓库之后跟着网页的提示初始化便可以了 3.add、commit、push三板斧 git add . //add仓库新增&#xff08;变…

V R元宇宙平台的未来方向|V R主题馆加 盟|游戏体验馆

未来&#xff0c;VR元宇宙平台可能会呈现出以下发展趋势和可能性&#xff1a; 全面融合现实与虚拟世界&#xff1a; VR元宇宙平台将更加无缝地融合现实世界和虚拟世界&#xff0c;用户可以在虚拟环境中进行各种活动&#xff0c;与现实世界进行互动&#xff0c;并且体验到更加逼…

【Linux进阶之路】理解UDP,成为TCP。

前言 学了TCP 和UDP之后&#xff0c;感觉UDP就像是初入职场的年轻人&#xff0c;两耳不闻 “窗外事”&#xff0c;只管尽力地把自己的事情做好&#xff0c;但收获的却是不可靠&#xff0c;而TCP更像是涉世极深的"职场老油条"&#xff0c;给人的感觉就是 “城府极深&a…

【Java.mysql】——数据删改(DU) 附加数据库约束

目录 &#x1f6a9;更新(Update) &#x1f6a9;删除&#xff08;Delete&#xff09; &#x1f6a9;数据库约束 &#x1f388;约束类型 ✅NULL约束 ✅NNIQUE 唯一约束 ✅DEFAULT&#xff1a;默认值约束 ✅PRIMARY KEY&#xff1a;主键约束 ✅FOREIGN KEY&#xff1a;外键…

性价比高一点的diy台式主机怎么搭配?

怎么搭配一台性价比高一点的台式机 建议&#xff1a; 选择合适的CPU和GPU。根据实际需求选择相对较新的CPU和GPU型号&#xff0c;以确保能够运行目标应用程序和游戏。 合理选择内存和存储。根据预算选择适当的内存和存储容量。8GB或16GB内存对于一般计算和游戏使用足够了&…

YOLOv9改进策略:IoU优化 | Wasserstein Distance Loss,助力小目标涨点

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;基于Wasserstein距离的小目标检测评估方法 Wasserstein Distance Loss | 亲测在多个数据集能够实现涨点&#xff0c;对小目标、遮挡物性能提升明显 &#x1f4a1;&#x1f4a1;&#x1f4a1;MS COCO和PASC…

【深度学习】深度学习md笔记总结第2篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

腾讯云服务器多少钱一年?最新价格4核8G服务器646元15个月

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

JavaWeb解压缩漏洞之ZipSlip与Zip炸弹

前言 前面一篇博文《Android Zip解压缩目录穿越导致文件覆盖漏洞》介绍过 Android 系统 Zip 文件解压缩场景下的目录穿越漏洞&#xff0c;近期在学习 JavaWeb 代码审计的时候从 github 看到《OpenHarmony-Java-secure-coding-guide.md》中“从 ZipInputStream 中解压文件必须进…