【Java设计模式】三、简单工厂、工厂方法模式、抽象工厂模式

文章目录

  • 0、案例:咖啡屋
  • 1、简单工厂模式 + 静态工厂(不属于23种之列)
  • 2、工厂方法模式
  • 3、抽象工厂模式
  • 4、简单工厂模式 + 配置文件解除耦合
  • 5、JDK源码中对工厂模式的应用

0、案例:咖啡屋

模拟咖啡店点餐。咖啡有多种,抽象类,子类为各种咖啡。咖啡店类聚合咖啡类。类图如下:

在这里插入图片描述

定义咖啡抽象类:

public abstract class Coffee {

	//获取咖啡种类名称
	public abstract String getName();

	//加奶
	public void addMilk() {
		System.out.println("加奶");
	}
	//加糖
	public void addSugar() {
		System.out.println("加糖");
	}
	
}

各种咖啡:

public class AmericanCoffee extends Coffee{

	@Override
	public String getName(){
		return "美式";
	}
}
public class LatteCoffee extends Coffee{

	@Override
	public String getName(){
		return "拿铁";
	}
}

咖啡屋类,聚合咖啡抽象类:

public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
        	throw new RuntimeException("店里没这种咖啡");
        }
        return coffee;
    }
}

以上代码的缺陷是咖啡类和 + 咖啡屋内耦合太高。下面用工厂模式解耦合。

1、简单工厂模式 + 静态工厂(不属于23种之列)

即由一个工厂决定创建哪一种产品类型的实例。 包括:

  • 抽象产品(抽象类)
  • 具体产品(子类)
  • 具体工厂(创建产品并提供方法给调用者)

改进上面的咖啡案例,引入工厂类,让咖啡屋不再自己创建咖啡对象,而是直接从工厂获取,类图:

在这里插入图片描述

/**
 * 咖啡工厂类
 */
public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffee;
    }
}
//新的咖啡屋类
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        Coffee coffee =  factory.createCoffee(type);
        //加配料
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

到这儿,有个疑惑,咖啡抽象类或子类变时,SimpleCoffeeFacroty类不还得变?这和直接咖啡屋类有啥区别?不都是改一个类?多此一举?其实不然,如果有一百家咖啡屋,而你没有工厂,那需求变更时你就得改一百次代码,而有了工厂,你只需改工厂一个类就行。本质还是这个工厂类带来了解耦。简单工厂可扩展为静态工厂(即把创建对象的方法改为静态的):

public class SimpleCoffeeFactory {
	
	//静态的
    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffe;
    }
}

但这种模式下,工厂类还是得修改,并不符合开闭原则。

2、工厂方法模式

  • 定义一个接口或者一个抽象的工厂类,让它的实现类(也是一个工厂)来决定创建哪一个实例对象。
  • 根据每个工厂不同的方法,来产生不同的所需要的对象

角色有:

  • 抽象工厂:只提供创建产品的接口给外界调用
  • 具体工厂:实现抽象工厂,完成具体产品的创建
  • 抽象产品:咖啡类
  • 具体产品:美式、拿铁

继续完善案例:

在这里插入图片描述

抽象工厂,只提供一个方法:

public interface CoffeeFactory {

	Coffee createCoffee();   //生产咖啡对象
	
}

具体的工厂类,实现抽象工厂:美式咖啡工厂、拿铁咖啡工厂

//美式咖啡工厂,专门用来生产美式咖啡
public class LatteCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }
    
}

//拿铁咖啡工厂,专门用来生产拿铁咖啡
public class AmericanCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
    
}

注意现在的咖啡店类:1)、它依赖于抽象,聚合的是抽象工厂对象 2)、创建咖啡店对象,需要set传一个咖啡工厂对象

public class CoffeeStore {

    private CoffeeFactory factory;

	//通过构造方法来赋值
    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }
	
	//也可set
	public void setFactory(CoffeeFactory factory) {
		this.factory = factory;
	}
	

    public Coffee orderCoffee(String type) {
        Coffee coffee = factory.createCoffee();   //直接调抽象类的方法,到时是哪个子工厂,就能创建出哪种咖啡
        //加配料
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

测试类:

public class Client {

	public static void main(Stirng[] args) {
		
		//创建咖啡店对象
		CoffeeStore store = new CoffeeStore();
		//创建具体的咖啡工厂
		CoffeeFactory factory = new AmericanCoffeeFactory();
		store.setFactory(factory);
		//点咖啡
		Coffee coffee = store.orderCoffee();
		//获取咖啡名称
		System.out.println(coffee.getName());
	}
}

此时,再有新品种咖啡进来,只需新增代码NewCoffeeFactory去实现CoffeeFactory,以及新增Coffee的子类NewCoffee。测试类中自然就是:

//创建具体的咖啡工厂
CoffeeFactory factory = new NewCoffeeFactory();
store.setFactory(factory);
//....

以上无须对原有的工厂做任何修改,符合开闭原则,并不会修改之前的代码。而缺点则是每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度(类太多,类爆炸)。

3、抽象工厂模式

前面的工厂模式,生产的都是相同系列的对象,如咖啡工厂类只生产各种咖啡,课程工厂只生产Java课程、python课程等。抽象工厂模式则是提供创建一系列相关或相互依赖对象的接口。比如华为除了手机,还有笔记本。骆驼除了外套还有鞋子、裤子。换句话:工厂模式处理的是同一级别的产品制造,而抽象工厂模式则是用来处理同一产品族对象的生产的。

在这里插入图片描述

角色有:

  • 抽象工厂:提供创建多个产品的接口给外界调用
  • 具体工厂:实现抽象工厂的多个抽象方法,完成具体产品的创建
  • 抽象产品:咖啡类、甜品类
  • 具体产品:美式、拿铁,甜品1、甜品2

继续完善咖啡案例,现在咖啡店除了咖啡外,还要售卖甜品,包括提拉米苏甜品、抹茶慕斯甜品。如果用上面的工厂模式,则需要定义甜品抽象类、两个甜品类(实现类)、两个甜品的工厂类。考虑用抽象工厂,以产品族的视角看,拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。类图:

在这里插入图片描述

先定义抽象工厂类,可对外返回咖啡、甜品这一个产品族的对象:

public interface DessertFactory {

    Coffee createCoffee();

    Dessert createDessert();
}

不同的公司或品牌,实现抽象工厂(具体工厂):

//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();   //美式甜点工厂生产美式
    }

    public Dessert createDessert() {
        return new MatchaMousse();  //美式甜点工厂生产
    }
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    public Dessert createDessert() {
        return new Tiramisu();
    }
}

产品实体类的定义同上,跳过。测试:

public class Client {

	public static void main(Stirng[] args) {
		
		//创建工厂对象
		DessertFactory store = new AmericanDessertFactory();
		//生产咖啡
		Coffee coffee = store.createCoffee();
		//生产甜品
		Dessert dessert = store.createDessert();
	}
}

以后再增加一个公司或者品牌,只需新增一个具体工厂类与对应的产品类。抽象工厂的优缺点:

  • 优点:引入产品族的概念后,不容易类爆炸。且客户端可获取到同一个产品族的对象。不会得到西装公司的西裤 + 运动风公司的运动鞋对象这种奇葩组合。
  • 缺点:产品族中新加一种产品时,就得修改所有的工厂类。比如现在除了咖啡、甜品外,还有新产品汉堡,就得改所有的抽象工厂和具体工厂

当需要创建的对象是一系列相互关联或相互依赖的产品族时,考虑抽象工厂模式,如电器工厂中的电视机、洗衣机、空调等。

4、简单工厂模式 + 配置文件解除耦合

前面提到简单工厂模式下,工厂对象和产品对象耦合度太高。这里用配置文件垫一下来解除耦合。类路径下创建文件bean.properties:

american=com.domain.bean.AmericanCoffee
latte=com.plat.domain.bean.LatteCoffee

静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次(注意下面createCoffee方法获取map中的对象时,获取到的是同一个对象,属于单例,不想单例就别把反射创建对象放静态代码块里)。

public class CoffeeFactory {
	
	//定义Map充当容器
    private static Map<String,Coffee> map = new HashMap();
	
	//加载properties文件 ⇒ 拿到类名 ⇒ 反射创建对象 ⇒ 存map
    static {
        Properties p = new Properties();
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            p.load(is);
            //遍历Properties集合对象
            Set<Object> keys = p.keySet();  
            for (Object key : keys) {
                //根据键获取值(全类名)
                String className = p.getProperty((String) key);
                //获取字节码对象
                Class clazz = Class.forName(className);
                Coffee obj = (Coffee) clazz.newInstance();
                map.put((String)key,obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	//对外的静态方法
    public static Coffee createCoffee(String name) {

        return map.get(name);
    }
}

测试:

public class Client {

	public static void main(Stirng[] args) {
		
		Coffee coffee = CoffeeFactory.createCoffee("american");
		System.out.println(coffee);
	}
}

如此,也符合开闭原则,改配置文件即可。

5、JDK源码中对工厂模式的应用

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

以上,获取迭代器对象,用到了工厂模式。

在这里插入图片描述

Collection接口为抽象工厂:

在这里插入图片描述

ArrayList为创建具体产品(迭代器)的具体工厂:

在这里插入图片描述

Iterator接口为抽象产品类,定义了该产品对象(迭代器对象)有的方法。Iter内部类则是具体的产品。

在这里插入图片描述

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

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

相关文章

day6 数组 嵌套循环

1&#xff1a;打印杨辉三角 91 int arr[6][6];92 int i,j0;93 for(i0;i<6;i)94 {95 for(j0;j<i;j) 96 {97 if(j0||ij)98 {99 arr[i][j]1; …

14 数值稳定性 + 模型初始化和激活函数【李沐动手学深度学习v2笔记】

1. 数值稳定性 神经网络的梯度 向量对向量求导&#xff08;梯度&#xff09;得到矩阵&#xff0c;太多的矩阵进行乘法会导致常见的两个问题 梯度消失和梯度爆炸 MLP MLP使用ReLU作为激活函数 梯度爆炸的问题 输入很大的时候梯度接近为0 梯度消失 梯度消失的问题 只能训练比…

一个强大的 VS Code 的AI代码插件:Fitten Code

AI 代码助手有 GitHub Copilot&#xff0c;一直想用&#xff0c;但是要爬梯子&#xff0c;还要收费。 一款国产AI写代码神器 Fitten Code&#xff0c;满足了我对AI写代码的幻想。它在功能上基本和 GitHub Copilot 差不多。 重要是的是&#xff0c;免费&#xff0c;免费&#…

【Web - 框架 - Vue】随笔 - Vue的简单使用 - 快速上手

【Web - 框架 - Vue】随笔 - Vue的简单使用 - 快速上手 Vue模板代码 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>模板</title> </head> <body> <div></div>…

安装Docker及DockerCompose

0.安装Docker Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道…

09 Qt扩展LineEdit组件:Input输入框

系列文章目录 01 Qt自定义风格控件的基本原则-CSDN博客 02 从QLabel聊起&#xff1a;自定义控件扩展-图片控件-CSDN博客 03 从QLabel聊起&#xff1a;自定义控件扩展-文本控件-CSDN博客 04 自定义Button组件&#xff1a;令人抓狂的QToolButton文本图标居中问题-CSDN博客 0…

数仓项目6.0(一)

尚硅谷大数据项目【电商数仓6.0】企业数据仓库项目_bilibili 数据流转过程 用户➡️业务服务器➡️数据库存储➡️数仓统计分析➡️数据可视化 数据仓库处理流程&#xff1a;数据源➡️加工数据➡️统计筛选数据➡️分析数据 数据库不是为了数据仓库服务的&#xff0c;需要…

【开源】SpringBoot框架开发数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

MATLAB环境下基于离散小波变换的心电信号伪影去除及PQRST波检测

可穿戴个人健康监护系统被广泛认为是下一代健康监护技术的核心解决方案。监护设备不断地感知、获取、分析和存储大量人体在日常活动中的生理数据&#xff0c;为人体的健康状况提供必要的、准确的、集成的和长期的评估和反馈。在心电监测领域&#xff0c;可穿戴传感器具有以下应…

C及C++每日练习(1)

一.选择&#xff1a; 1.以下for循环的执行次数是&#xff08;&#xff09; for(int x 0, y 0; (y 123) && (x < 4); x); A.是无限循环 B.循环次数不定 C.4次 D.3次 对于循环&#xff0c;其组成部分可以四个部分&#xff1a; for(初始化;循环进行条件;调整) …

JavaScript实现的计时器效果

之前做过电商网站倒计时的效果&#xff0c;今天在倒计时的基础上&#xff0c;把代码修改了一下&#xff0c;改为计时器效果&#xff0c;实现了以下功能&#xff1a; 1.点击“开始”后&#xff0c;按秒计时且“开始”文字变为“停止”&#xff1b; 2.点击“停止”&#xff0c;计…

【Python实战】——Python+Opencv是实现车牌自动识别

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

自动化测试过程中的手机验证码处理!

手机验证码登录很普遍了&#xff0c;那么在自动化测试的时候需要登录&#xff0c;登录不了就意味着很多自动化就没法执行下去了。 到底该怎么处理呢&#xff1f;其实并不难&#xff0c;我们先看下验证码的业务逻辑&#xff0c;在我们“点击获取验证码”按钮的时候&#xff0c;…

LeetCode刷题-206.反转链表【递归实现】

206.反转链表 题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 示例1 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例2 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例3 输入&#xff1a;hea…

【C语言】动态内存管理------常见错误,以及经典笔试题分析,柔性数组【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】动态内存管理------常见错误&#xff0c;以及经典笔试题分析&#xff0c;柔性数组【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 在了解完内存操作中最关键的一节---动…

微信客户维护的三个关键点,助你提高转化率!

对于微信客户维护&#xff0c;有三个关键点尤为重要&#xff0c;它们能够有效提高客户转化率&#xff0c;让客户服务更加高效和个性化。接下来&#xff0c;让我们一起来了解这三个关键点。 1、 给客户打标签 在日常的客户维护中&#xff0c;给客户打标签是非常重要的。通过给…

BioTech - 药物晶型预测与剂型设计 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/136441046 药物晶型预测与剂型设计是指利用计算机模拟和优化药物分子在固态形式下的结构、性质和稳定性&#xff0c;以及与制剂工艺和质…

【Python】外网远程登录访问jupyter notebook+pycharm使用ipython

第一步&#xff1a;创建python虚拟环境 conda create -n py3610 python3.6.10第二步&#xff1a;安装ipython pip install ipython pip install ipython notebook第三步&#xff1a;创建 IPython Notebook 服务器配置文件 # 进入python交互shell&#xff0c;设置密码 >&…

基于springboot+vue的校园失物招领系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

HLS的硬件加速器设计

完整可点击跳转 目录 硬件加速器的设计方法高层次综合HLSHLS与电路地对应关系HLS的设计规范HLS优化延迟优化降低单个循环的延迟循环展开(Unroll)循环展平(Flatten)多个循环的并行化循环合并循环函数化数据流执行(Dataflow)吞吐量优化循环/函数流水线数据流优化调试硬件加…