【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{@Overridepublic String getName(){return "美式";}
}
public class LatteCoffee extends Coffee{@Overridepublic 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;}//也可setpublic 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文件 ⇒ 拿到类名 ⇒ 反射创建对象 ⇒ 存mapstatic {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内部类则是具体的产品。

在这里插入图片描述

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

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

相关文章

CentOS 8使用笔记

查看磁盘空间 df -h查看python版本 python3 --version查看某个端口是否打开 nc -zv localhost 9200或者 curl http://localhost:9200查看所有打开的端口并将部分端口升序排列 ss -tuln | awk NR>1 | sort -k 2,2n -k 1,1添加端口并刷新 firewall-cmd --zonepublic --a…

掌握React中的useCallback:优化性能的秘诀

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

011-keep-alive详解

keep-alive详解 1、简介2、keep-alive的使用效果未使用keep-alive的效果图使用keep-alive的效果图include和exclude指定是否缓存某些组件使用keep-alive的钩子函数执行顺序问题 3、keep-alive的应用场景举例4、总结 1、简介 keep-alive 是 Vue 的内置组件&#xff0c;当它包裹…

Java开发从入门到精通(一):Docker

Docker 目录&#xff1a; Docker 简介 Docker 常见指令 Docker 运行原理 Docker 网络 可视化界面 Docker和k8s区别&#xff1f; 1 Docker 简介 1.1 Docker 由来 Docker 是基于 Go 语言开发的一个容器引擎&#xff0c;Docker是应用程序与系统之间的隔离层。通常应用程序对安装…

深入浅出计算机网络 day.2 概论⑤ 计算机网络的性能指标

请等一等&#xff0c; 用一个完整的春天 捣碎麦田 —— 24.3.10 一、计算机网络的性能指标 上 计算机网络的性能指标被用来从不同方面度量计算机网络的性能 常用的八个计算机网络性能指标 速率 比特&#xff08;bit&#xff09;是计算机中数据量的基本单位&#xff0c;一个比特…

算法D38| 动态规划1 | 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

理论基础 无论大家之前对动态规划学到什么程度&#xff0c;一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目&#xff0c;看我讲的理论基础&#xff0c;会有感觉 是不是简单题想复杂了&#xff1f; 其实并没有&#xff0c;我讲的理论基础内容&#xff0c;在动…

TensorFlow 量化投资分析

文章目录 一、TensorFlow 量化投资的一般步骤二、TensorFlow 如何建立特征工程三、TensorFlow 构建量化投资模型简单示例 一、TensorFlow 量化投资的一般步骤 数据准备&#xff1a;收集和整理用于训练和测试模型的金融数据&#xff0c;例如股票价格、财务指标等。特征工程&…

03- javaBean 新花样? record 新特性

定义和特性 JDK16 最终增加了record关键字&#xff0c;record定义的类希望成为数据传输对象 也叫数据载体&#xff0c;使用record 时候&#xff0c;编译器会自动生成&#xff1a; 不可变的字段一个规范的构造器每个元素(组件)都有访问方法equalshashCodetoString public rec…

SQL 注入攻击 - insert注入

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、注入原理 描述:insert注入是指通过前端注册的信息被后台通过insert操作插入到数据库中。如果后台没有做相应的处理,就可能导致insert注入漏洞。原因:后台未对用户输入进行充…

python爬虫(4)

#前期先说明一下为啥爬虫需要学习数组的存储和处理&#xff0c;只是说在你后期接触到最简单的爬虫后有一个地方可以存放你的数据# 下面为大家带来一个我在做excel表整理时的代码以及上次代码的结果 上次代码的结果&#xff1a; 新的代码&#xff1a; import numpy as np im…

使用51单片机控制lcd1602字体显示

部分效果图&#xff1a; 准备工作&#xff1a; 51单片机&#xff08;BST&#xff09;1602显示屏 基础知识&#xff1a; 注&#xff1a;X表示可以是0&#xff0c;也可以是1&#xff1b; DL 1&#xff0c; N 1&#xff0c; F 0&#xff0c; 代码一&#xff1a; 要求显示字母…

MySQL下载及安装

引言 在当今数据驱动的世界里,数据库管理系统(DBMS)扮演着至关重要的角色。MySQL,作为一个广泛使用的关系型数据库管理系统,因其强大的性能、可靠性以及易用性,在各种应用场景中都有着重要的地位。无论是小型项目、网站还是大型的企业级应用,MySQL都能够提供高效的数据存…

网络安全审计是什么意思?与等保测评有什么区别?

网络安全审计和等保测评在信息安全领域中都是非常重要的环节。但不少人对于这两者是傻傻分不清楚&#xff0c;今天我们就来简单聊聊网络安全审计是什么意思&#xff1f;与等保测评有什么区别&#xff1f; 网络安全审计是什么意思&#xff1f; 网络安全审计是通过对网络系统和网…

学习和认知的四个阶段,以及学习方法分享

本文分享学习的四个不同的阶段&#xff0c;以及分享个人的一些学习方法。 一、学习认知的四个阶段 我们在学习的过程中&#xff0c;总会经历这几个阶段&#xff1a; 第一阶段&#xff1a;不知道自己不知道&#xff1b; 第二阶段&#xff1a;知道自己不知道&#xff1b; 第三…

数据结构部分

来源地址 一 数据结构 1 堆和树之间的区别 区别就在于树是没有特定顺序的&#xff0c;你需要遍历整个树才能找到特定元素&#xff1b;而堆是有序的&#xff0c;你可以直接找到最大&#xff08;或最小&#xff09;的元素。 堆&#xff1a;假设你正在开发一个任务调度系统&…

JimuReport积木报表 v1.7.2 版本发布,低代码报表工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

YoLo进化史《A COMPREHENSIVE REVIEW OF YOLO: FROM YOLOV1 TOYOLOV8 AND BEYOND》

Abstract YOLO已成为机器人、无人驾驶汽车和视频监控应用的核心实时目标检测系统。我们对YOLO的发展进行了全面的分析&#xff0c;研究了从最初的YOLO到YOLOv8的每次迭代中的创新和贡献。我们首先描述标准指标和后处理;然后&#xff0c;我们讨论了网络架构的主要变化和每个模型…

C++面试题和笔试题(一)

今天面试了一家100人以上的小公司&#xff0c;做QT上位机开发&#xff0c;个人感觉凉凉。以下是笔试题和我能回忆起的面试题 目录 一、笔试题 1. 什么是C中的指针 官方解释&#xff1a; 自己的理解&#xff1a; 2.什么是引用&#xff0c;它与指针有什么不同 官方解释&…

SpringCloud-Alibaba-Nacos教程

SpringCloud-Alibaba-Nacos教程 下载地址 https://github.com/alibaba/nacos/releases/tag/2.2.3 直接进入bin包 运行cmd命令 startup.cmd -m standalone 运行成功后 进入nacos可视化页面 账号密码默认都是nacos http://localhost:8848/nacos 微服务入驻Nacos服务注册…

阿尔巴尼亚借助ChatGPT加快欧盟入会进程

原文&#xff1a;https://www.euractiv.com/section/politics/news/albania-to-speed-up-eu-accession-using-chatgpt/ 来源&#xff1a;https://weibo.com/1727858283/O3ZoWp6oO?refer_flag1001030103_ 阿尔巴尼亚政府计划利用ChatGPT技术&#xff0c;将成千上万页的欧盟法律…