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

news2025/1/21 18:06:06

目录

  • 1.概念
  • 2.结构
  • 3.实现
  • 4.优缺点
  • 5.使用场景
  • 6.模式扩展
  • 7.JDK源码解析——Collection.iterator方法

1.概念

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

(2)本文要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。具体来说,抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构

在这里插入图片描述

(3)抽象工厂模式是工厂方法模式的升级版本,它们之间有以下几个区别:

  • 粒度不同:抽象工厂模式关注一族相关对象的创建,一个工厂负责创建一族产品对象;而工厂方法模式关注单个对象的创建,每个工厂只负责创建一种产品。
  • 抽象程度不同:抽象工厂模式具有更高的层次和更大的封装性,它通过引入抽象工厂和具体工厂的概念,将一族产品对象的创建交给抽象工厂来完成;而工厂方法模式的抽象程度相对较低,它通过定义一个工厂接口或抽象类来声明创建产品的方法,然后具体工厂类会实现这个接口或抽象类来创建具体产品对象。
  • 关注点不同:抽象工厂模式关注的是一族产品对象的创建,它解决的是多个产品对象之间的组合问题,确保一族产品对象能够相互协作;而工厂方法模式关注的是单个产品对象的创建,它解决的是产品扩展和变化的问题。
  • 扩展性不同:抽象工厂模式的扩展性更强,可以同时添加新的具体工厂和产品类,以及扩展一族产品的组合方式;而工厂方法模式的扩展性相对较低,当需要添加新的产品时,需要新增对应的具体工厂类和具体产品类。

综上所述,抽象工厂模式和工厂方法模式在粒度、抽象程度、关注点和扩展性等方面存在差异。选择使用哪种模式取决于具体的业务需求和设计要求。如果需要创建一族相关的产品对象,并确保这些产品对象能够相互协作,可以考虑使用抽象工厂模式;如果只需要创建单个产品对象,并且希望能够轻松扩展和添加新的产品类,可以选用工厂方法模式。

2.结构

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

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

3.实现

现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:
在这里插入图片描述
部分核心代码如下:

抽象工厂:DessertFactory.java

package com.itheima.patterns.factory.abstract_factory;

//抽象工厂类
public interface DessertFactory {
    //生产咖啡的功能
    Coffee createCoffee();
    
    //生产甜品的功能
    Dessert createDessert();
}

具体工厂:AmericanDessertFactory.java

package com.itheima.patterns.factory.abstract_factory;

//美式风味的甜品工厂,可以生产美式咖啡和抹茶慕斯
public class AmericanDessertFactory implements DessertFactory{
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
    
    @Override
    public Dessert createDessert() {
        return new Matchamousse();
    }
}

具体工厂:ItalyDessertFactory.java

package com.itheima.patterns.factory.abstract_factory;

//意大利风味甜品工厂,可以生产拿铁咖啡和提拉米苏甜品
public class ItalyDessertFactory implements DessertFactory{
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
    
    @Override
    public Dessert createDessert() {
        return new Trimisu();
    }
}

Client.java

package com.itheima.patterns.factory.abstract_factory;

public class Client {
    public static void main(String[] args) {
        //创建的是意大利风味甜品工厂对象
        ItalyDessertFactory factory = new ItalyDessertFactory();
        //获取拿铁咖啡和提拉米苏甜品
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();
    
        System.out.println(coffee.getName());
        dessert.show();
    }
}

输出结果如下:

public class Client {
    public static void main(String[] args) {
        //创建的是意大利风味甜品工厂对象
        ItalyDessertFactory factory = new ItalyDessertFactory();
        //获取拿铁咖啡和提拉米苏甜品
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());
        dessert.show();
    }
}

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

4.优缺点

(1)优点:

  • 封装性强:抽象工厂模式将一族相关产品的创建逻辑封装在一个工厂中,客户端通过抽象工厂来创建产品对象,无需关心具体的产品类,有效降低了客户端与具体产品类之间的耦合性。
  • 产品族之间的一致性:抽象工厂模式保证创建的产品对象属于同一产品族,它们之间是相互匹配的,因此能够保证创建的产品对象之间能够正常协作。
  • 灵活性高:通过切换具体工厂,可以在运行时创建不同的产品族,使系统具有较高的灵活性和可扩展性。

(2)缺点:

  • 可扩展性受限:增加新的产品等级结构(新的抽象产品)较为困难,需要修改抽象工厂接口及其所有具体工厂的代码,违背了开闭原则。
  • 复杂性增加:随着产品族和产品等级结构的增多,抽象工厂和具体工厂的数量会增加,导致系统的复杂度增加。
  • 不易于单独新增产品:想要添加单独的产品类需要修改抽象工厂接口及其所有具体工厂的代码。

5.使用场景

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

6.模式扩展

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

(2)具体步骤如下:

  • 定义配置文件 bean.properties
american=com.itheima.patterns.factory.config_factory.AmericanCoffee
latte=com.itheima.patterns.factory.config_factory.LatteCoffee
  • 改进工厂类
package com.itheima.patterns.factory.config_factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

public class CoffeeFactory {
    
    //加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储
    //1.定义容器对象来存储咖啡对象
    private static HashMap<String,Coffee> map = new HashMap<String, Coffee>();
    
    //2.加载配置文件,只需要加载一次
    static {
        //2.1.创建Properties对象
        Properties properties = new Properties();
        //2.2.调用properties对象中的load方法来加载配置文件
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            //从properties集合中获取全类名并创建对象
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                String className = properties.getProperty((String) key);
                //通过反射技术创建对象
                Class clazz = Class.forName(className);
                Coffee coffee = (Coffee) clazz.newInstance();
                //将名称和对象存储到容器中
                map.put((String) key, coffee);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    //根据名称获取对象
    public static Coffee createCoffee(String name) {
        return map.get(name);
    }
}
  • 测试
package com.itheima.patterns.factory.config_factory;

public class Client {
    public static void main(String[] args) {
        Coffee coffee1 = CoffeeFactory.createCoffee("american");
        System.out.println(coffee1.getName());    //美式咖啡
        Coffee coffee2 = CoffeeFactory.createCoffee("latte");
        System.out.println(coffee2.getName());    //拿铁咖啡
    }
}

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

有关 Properties 类的具体知识可以参考 Java 基础——Properties 类这篇文章。

7.JDK源码解析——Collection.iterator方法

(1)首先来看一下这一段代码:

package com.itheima.patterns.factory.iteratordemo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Tom");
        list.add("Mike");
        list.add("Marry");
    
        //获取迭代器对象
        Iterator<String> iterator = list.iterator();
        //使用迭代器遍历
        while(iterator.hasNext()){
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

大家应该很熟悉上面的代码,它使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。现在通过类图来看其中的结构:
在这里插入图片描述

Collection 接口是抽象工厂类,ArrayList 是具体的工厂类;Iterator 接口是抽象商品类,ArrayList 类中的 Iter 内部类是具体的商品类。在具体的工厂类中 iterator() 方法创建具体的商品类的对象。

  • Iterator.java
    在这里插入图片描述
  • Collection.java
    在这里插入图片描述
  • ArrayList.java
    在这里插入图片描述
    在这里插入图片描述

注:DateForamt 类中的 getInstance() 方法、Calendar 类中的 getInstance() 方法使用的也都是工厂模式。

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

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

相关文章

MATLAB m文件格式化

记录一个网上查到的目前感觉挺好用的格式化方法。 原链接&#xff1a; https://cloud.tencent.com/developer/article/2058259 压缩包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ZpQ9qGLY7sjcvxzjMPAitw?pwd6666 提取码&#xff1a;6666 下载压缩包&#xf…

【GDB】自定义命令

.gdbinit 文件的使用 gdb自定义命令 自定义命令格式 define <command> <code> end document <command> <help text> end示例 .gdbinit 脚本中输入如下内容 layout asm b 5define print-tyustliecho hello, world\n enddocument print-tyustliusage…

【冒烟测试前置】如何把控提测质量?

你是否碰到过开发提测速度很快&#xff0c;导致项目排队&#xff0c;结果介入测试时&#xff0c;第一条用例都跑不通的情况&#xff1f; 你是否碰到过因为开发提测质量差&#xff0c;导致反复修改&#xff0c;反复提测&#xff0c;反复重复验证的情况&#xff1f; 你是否碰到…

sql数据类型,约束以及单表查询

嘎嘎学撒 数据类型约束条件DML 数据操作语句一、插入数据INSERT二、更新数据UPDATE三、删除数据DELETE四、MySQL单表查询五、关键词 数据类型 常见的数据类型 数值类型&#xff1a; 整数类型 TINYINT SMALLINT MEDIUMINT INT BIGINT 整型可以指定是有符号的和无符号的&#xf…

如何快速做跨业务测试?

当业务任务多且人力资源不充足的情况下&#xff0c;不同业务的同学可能需要去不同的业务进行临时支援&#xff0c;可能在时间方面有长有短&#xff0c;但是如何迈出第一步是很多人需要关心的一件事。 本文以实际跨业务测试经验&#xff08;订单业务测试人员如何测试售后业务&a…

【操作】国标GB28181视频监控EasyGBS平台更新设备信息时间间隔

国标GB28181协议视频平台EasyGBS是基于GB28181协议的视频监控云服务平台&#xff0c;可支持多路设备同时接入&#xff0c;并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。平台可提供视频监控直播、云端录像、云存储、检索回放、智能告警、语音对讲、平台…

[Python进阶] Pyinstaller打包程序时为程序添加图标

5.5 Pyinstaller打包程序时为程序添加图标 5.5.1 程序图标的好处 增强可识别性&#xff1a;图标是一种视觉语言&#xff0c;能够提高应用程序的可识别性&#xff0c;使其在众多应用程序中更容易被用户找到和识别。 帮助用户理解应用程序功能&#xff1a;图标可以快速传达应用…

硬件基本功--电阻/电容/电感/二极管

一、电阻 电阻的主要参数&#xff1a;阻值、精度、封装、功率、耐压 电阻在电路中的作用&#xff1a;分压、限流、采样、偏置等等 阻值&#xff1a;103 10*1000 10KΩ 电阻流过电流&#xff0c;就会有压降&#xff0c;从而产生功率损耗 电阻封装、功率、耐压的关系&#xff1…

青云1000----华为昇腾310 注意事项

青云1000帮助文档 只是一部分&#xff0c;后续遇到的问题会补充 注意事项&#xff01;&#xff01;&#xff01;&#xff01; type-c只用于数据传输不能供电DC供电和锂电池不能同时供电&#xff0c;会烧掉风扇正负级不要插反 账户密码 HwHiAiUser 密码Mind123 TypeC USB …

接口测试——接口协议抓包分析与mock_L2

目录&#xff1a; 抓包工具charles抓包工具fiddler抓包工具证书配置app抓包实战练习接口测试实战练习 1.抓包工具charles 工具介绍 支持 SSL 代理支持流量控制支持重发网络请求&#xff0c;方便后端调试支持修改网络请求参数支持网络请求的截获并动态修改可以自动将 json 或…

七、栈与队列(stack and queue)

文章目录 一、栈与队列基础二、例题&#xff08;一&#xff09;栈1.[232. 用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/description/)&#xff08;1&#xff09;思路&#xff08;2&#xff09;代码&#xff08;3&#xff09;复杂度分析 2.[225. …

海大校园学习《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

海大校园学习《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

pytorch固定随机数中种子

1、添加到yolov7的utils/general.py文件最下面 import pkg_resources as pkg def check_version(current0.0.0, minimum0.0.0, nameversion , pinnedFalse, hardFalse, verboseFalse):# Check version vs. required versioncurrent, minimum (pkg.parse_version(x) for x in …

【数据结构--八大排序】之归并排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

中国社科院与美国杜兰大学完成了我的金融硕士梦

一个人从出生就被赋予着太多的期待&#xff0c;比如爸爸妈妈从小没有完成的梦想&#xff0c;哥哥姐姐失败的课程&#xff0c;为了实现这些期待&#xff0c;人们忙着奋斗、拼搏、追逐&#xff0c;马不停蹄去做有用的事&#xff0c;结交有用的人&#xff0c;不敢虚度半寸光阴&…

第1章 数据结构绪论

1.1 开场白 1.2 你数据结构怎么学的 1.3 数据结构起源 早期人们都把计算机理解为数值计算工具&#xff0c;就是感觉计算机当然是用来计算的&#xff0c;所以计算机解决问题&#xff0c;应该是先从具体问题中抽象出一个适当的数据模型&#xff0c;设计出一个解此数据模型的算…

crypto:丢失的MD5

题目 得到一个md5.py 运行一下&#xff0c;发现报错&#xff0c;修改一下 运行之后又报错 报错原因是算法之前编码 正确的代码为 import hashlib for i in range(32,127):for j in range(32,127):for k in range(32,127):mhashlib.md5()m.update((TASC chr(i) O3RJMV c…

Makerbase SimpleFOC ESP32 例程12 双霍尔电机位置控制

Makerbase SimpleFOC ESP32 例程12 双霍尔电机位置控制 第 1 部分 硬件介绍 1.1 硬件清单 序号品名数量1MKS ESP32 FOC V1.0 主板12LA034-040NN07A 霍尔电机23DC24V电源14USB 线1 硬件清单如下图所示&#xff1a; ESP32 FOC V1.0主板说明书等更多资料请加入企鹅&#xff…

1039 到底买不买

描述 小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串&#xff0c;但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下&#xff0c;某串珠子里是否包含了全部自己想要的珠子&#xff1f;如果是&#xff0c;那么告诉她有多少多余的珠子&#xff1…

对比学习的锚网络和自动生成标签

文章目录 锚网络自动生成标签&#xff08;词组&#xff09; 锚网络 在对比学习&#xff08;Contrastive Learning&#xff09;中&#xff0c;“锚网络”&#xff08;Anchor Network&#xff09;通常是指一个用于生成数据样本的表示的网络。锚网络的主要作用是将输入数据样本转…