惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
O
OpenAI News
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
M
MIT News - Artificial intelligence
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 司徒正美
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
Security Latest
Security Latest
T
Tailwind CSS Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
W
WeLiveSecurity
N
News and Events Feed by Topic
aimingoo的专栏
aimingoo的专栏
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
爱范儿
爱范儿
宝玉的分享
宝玉的分享
腾讯CDC
H
Heimdal Security Blog
Webroot Blog
Webroot Blog
AI
AI
WordPress大学
WordPress大学
Recorded Future
Recorded Future
SecWiki News
SecWiki News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
C
Check Point Blog
TaoSecurity Blog
TaoSecurity Blog
Cisco Talos Blog
Cisco Talos Blog
The Cloudflare Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - Franky
云风的 BLOG
云风的 BLOG

Java技术经验分享

生产故障处理SOP分享 | Java技术经验分享 系统稳定性建设实践总结【转载】 | Java技术经验分享 valine访问leancloud国际版异常,评论失效修复 | Java技术经验分享 匆匆忙忙的2021 | Java技术经验分享 聊一下换工作 | Java技术经验分享 IoT系列(2):WIFI设备常见配网方案介绍 | Java技术经验分享 IoT系列(1):什么是物联网 | Java技术经验分享 Java8特性2 - StreamApi | Java技术经验分享 Java8特性1 - lambda表达式&函数式接口 | Java技术经验分享 设计模式(5)-适配器模式 | Java技术经验分享 设计模式(4)-建造者模式 | Java技术经验分享 设计模式(3)-原型模式与浅拷贝和深拷贝 | Java技术经验分享 设计模式(1)-带你了解3类8种单例模式 | Java技术经验分享 Java时间处理5---Java8中时区相关类库介绍 | Java技术经验分享 Java时间处理4---Java8中LocalDate、LocalTime、LocalDateTime介绍 | Java技术经验分享 Java时间处理3---Java8中Instant、Duration、Period、Clock介绍 | Java技术经验分享 一些有意思的问答 | Java技术经验分享 Nacos系列博客说明 | Java技术经验分享 菜鸡程序员的2019年度总结 | Java技术经验分享 Java中“附近的人”实现方案讨论及代码实现 | Java技术经验分享 Java时间处理2----时区TimeZone类方法探究(Java8以前) | Java技术经验分享 Java时间处理1----Date和Calendar方法探究(Java8以前) | Java技术经验分享 FastJson中JSONString、JavaBean、JSONObject、JSONArray的转换关系及API示例 | Java技术经验分享 2019.11软考软件设计师归来心得体会及复习备考指南 | Java技术经验分享 你还没用过“约定式提交”吗?那你赶紧来补补知识吧 | Java技术经验分享 教你如何看懂UML中的类图及类图中的关系 | Java技术经验分享 设计模式总览 | Java技术经验分享 萌新入门Github请看这里,学不会远程教 | Java技术经验分享 Hexo的工作原理探究 | Java技术经验分享 Hexo-theme-butterfly修改调整记录教程 | Java技术经验分享 排序8:基数排序 | Java技术经验分享 排序7:归并排序 | Java技术经验分享 排序6:快速排序 | Java技术经验分享 排序5:冒泡排序 | Java技术经验分享 排序4:堆排序 | Java技术经验分享 排序3:选择排序 | Java技术经验分享 排序2:希尔排序 | Java技术经验分享 排序1:直接插入排序 | Java技术经验分享 推荐一款博客一文多发的良心工具OpenWrite | Java技术经验分享 近期学习计划 | Java技术经验分享 Nacos(九):Nacos集群部署和遇到的问题 | Java技术经验分享 Nacos(八):Nacos持久化 | Java技术经验分享 Nacos(七):Nacos共享配置 | Java技术经验分享 Nacos(六):多环境下如何“管理”及“隔离”配置和服务 | Java技术经验分享 Nacos(五):多环境下如何“读取”Nacos中相应的配置 | Java技术经验分享 Nacos(四):SpringCloud项目中接入Nacos作为配置中心 | Java技术经验分享 Nacos(三):Nacos与OpenFeign的对接使用 | Java技术经验分享 Nacos(二):SpringCloud项目中接入Nacos作为注册中心 | Java技术经验分享 Nacos(一):Nacos介绍 | Java技术经验分享 20190719小组分享 | Java技术经验分享 Java中equals和HashCode方法的分析 | Java技术经验分享 Java中==和equals方法的分析 | Java技术经验分享 Java中的自动拆装箱、装箱缓存 | Java技术经验分享 About-blog | Java技术经验分享 Java中的编译、反编译和反编译工具全家桶分享 | Java技术经验分享 finalize()的生命周期(执行过程) | Java技术经验分享 Java关键字之final、finally与finalize方法 | Java技术经验分享 Java中重写、重载 | Java技术经验分享 Java中面向对象的三大特征:继承、封装、多态 | Java技术经验分享 DockerFile介绍 | Java技术经验分享 Docker环境下安装Gitlab | Java技术经验分享 Docker中私有仓库的搭建流程 | Java技术经验分享 Centos7下两种方式安装Docker-CE | Java技术经验分享 Vert.x创建一个Http服务 | Java技术经验分享 Vert.x创建TCP服务端及客户端 | Java技术经验分享 Vert.x Core(二)- Event Bus(事件总线) | Java技术经验分享 Vert.x-Core(一)- 基础篇 | Java技术经验分享 SpringBoot项目中实现国际化 | Java技术经验分享 Vert.x介绍 | Java技术经验分享 毕设选题项目本地运行环境搭建教程 | Java技术经验分享 Jupyter Notebooks的安装和使用介绍 | Java技术经验分享 算法笔试题:1元,5元,10元,20元,50元、100元面值人民币组合给定x元的问题 | Java技术经验分享 Quartz学习总结 | Java技术经验分享 SpringBoot2.x集成Redis | Java技术经验分享 SpringBoot2.x集成MongoDB | Java技术经验分享 [SpringCloud学习] - 浅谈微服务架构 | Java技术经验分享 基于hexo和coding免费搭建个人博客网站 | Java技术经验分享 Hello World | Java技术经验分享
设计模式(2)-工厂模式图文介绍 | Java技术经验分享
文章作者: LarsCheng · 2020-08-30 · via Java技术经验分享

工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

模拟需求①

假设现有一个口罩生产工厂,可以生产防霾口罩、医用一次性口罩、N95口罩
客户可以通过口罩直营店根据自己的需求下单购买口罩
使用代码实现这一流程

传统实现方式

根据给出的需求,结合面向对象思想,大概有以下几个类

  • BaseMask 抽象口罩类
  • HazeMask 防霾口罩类
  • MedicalMask 医用口罩类
  • N95Mask N95口罩类
  • MaskStore 直营店类
  • Client 客户类

简单类图如下:

实现代码

HazeMask、MedicalMask、N95Mask继承自BaseMask,分别实现prepare方法,并调用setName方法设置name属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public abstract class BaseMask {
protected String name;
public abstract void prepare();
public void processing(){
System.out.println(name+"开始加工...");
}
public void bale(){
System.out.println(name+"打包完成...");
}
public void setName(String name) {
this.name = name;
}
}

MaskStore类,实现了口罩直营店根据用户需求进行下单的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MaskStore {
public void order() {
BaseMask mask = null;
int maskType;
do {
maskType = getType();
if (1 == maskType){
mask = new HazeMask();
}else if (2 == maskType){
mask = new MedicalMask();
}else if (3 == maskType){
mask = new N95Mask();
}else {
System.out.println("不支持的产品类型");
break;
}
mask.prepare();
mask.processing();
mask.bale();
} while (true);
}






private int getType() {
try {
BufferedReader typeReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入需要下单的类型: ");
return Integer.parseInt(typeReader.readLine());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

Client的实现就相对简单,模拟用户下单操作,直接调用直营店暴露的下单order方法

优缺点分析

根据场景需求我们有了如上的代码方案,其中涉及到的类和方法都比较好理解,核心主要是通过用户需要下单的type来进行产品的创建,但优缺点需要细细捋一捋

优点:思路清晰,便于理解
缺点:违反开闭原则,也就是扩展性差,如果添加一个新的口罩类型,涉及到的修改点过多

举个栗子:
如果这时候添加一个新的口罩类型,那所有的口罩直营店类中的代码都需要同步修改

这时候有一种解决方案:将根据类型创建产品的方法单独封装起来,当有新产品加入时,只需要修改单独封装过的这部分代码,而调用方可以做到无感知接入,这种方式也叫做简单工厂模式。但他并不属于23种设计模式,简单工厂仅仅指一种创建类的解决方案

简单工厂模式

相对于传统方案中多出一个简单工厂类SimpleMaskFactory,同时对MaskStore进行了重构,简单类图如下:

代码实现

与传统方案不同的是,之前的口罩产品创建是在MaskStore中,使用简单工厂模式后,将创建口罩产品的工作封装到了SimpleMaskFactory中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class SimpleMaskFactory {

public BaseMask createMask(int maskType) {
BaseMask mask = null;
if (1 == maskType) {
mask = new HazeMask();
} else if (2 == maskType) {
mask = new MedicalMask();
} else if (3 == maskType) {
mask = new N95Mask();
}
return mask;
}
}

MaskStore只需要持有工厂类和需要下单的产品类型,发起下单操作即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MaskStore {
private SimpleMaskFactory factory;

public MaskStore(SimpleMaskFactory factory) {
this.factory = factory;
}

public void order() {
BaseMask mask = null;
int maskType;
do {
maskType = getType();
mask = factory.createMask(maskType);
if (!Objects.isNull(mask)){
mask.prepare();
mask.processing();
mask.bale();
}else {
System.out.println("不支持的产品类型...");
break;
}
} while (true);
}

private int getType() {
try {
BufferedReader typeReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("----------------");
System.out.println("输入需要下单的类型: ");
return Integer.parseInt(typeReader.readLine());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

Client客户端的调用也更加方便

1
2
3
4
5
public class Client {
public static void main(String[] args) {
new MaskStore(new SimpleMaskFactory()).order();
}
}

模拟需求②

假设现有多个口罩生产工厂,大致分为杭州制造和上海制造,可以生产防霾口罩、医用一次性口罩,
客户可以通过自己的需求下单购买某个地址制造的某一种口罩
使用代码实现这一流程

此时的需求不仅有地域区分,同时还有种类区分,这种场景该如何处理呢?

  • 方案1
    • 使用简单工厂模式,根据地域创建不同的工厂类,通过不同的工厂类来进行不同的产品创建
    • 扩展性差,可维护性差
  • 方案2
    • 使用工厂方法模式,将创建产品的方法抽象化,创建对象的操作交给子类自己来完成,即将对象实例化推迟到子类

工厂方法模式

与简单工厂模式所不同,工厂方法模式将定义一个创建对象的抽象方法,根据实际需求整理到所涉及的类有

  • BaseMask 抽象的口罩类
  • HangzhouHazeMask 杭州制造-防霾口罩
  • HangzhouMedicalMask 杭州制造-医用口罩
  • ShanghaiHazeMask 上海制造-防霾口罩
  • ShanghaiMedicalMask 上海制造-医用口罩
  • BaseMaskFactory 抽象口罩工厂类,定义了一个创建对象的抽象方法,将对象创建延缓到子类进行
  • HangzhouMaskFactory 杭州制造工厂类
  • ShanghaiMaskFactory 上海制造工厂类
  • Client 客户类

简单的类图如下:

代码实现

HangzhouHazeMask、HangzhouMedicalMask、ShanghaiHazeMask、ShanghaiMedicalMask继承自BaseMask,分别实现prepare方法,并调用setName方法设置name属性

HangzhouMaskFactory、ShanghaiMaskFactory继承自BaseMaskFactory类,重写了抽象方法createMask方法实现自己的对象创建逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public abstract class BaseMaskFactory {

abstract BaseMask createMask(int maskType);

public BaseMaskFactory() {
BaseMask mask = null;
int maskType;
do {

maskType = getType();
mask = createMask(maskType);
if (!Objects.isNull(mask)) {
mask.prepare();
mask.processing();
mask.bale();
}else {
System.out.println("不支持的产品类型...");
break;
}
} while (true);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

public class HangzhouMaskFactory extends BaseMaskFactory{
@Override
BaseMask createMask(int maskType) {
BaseMask mask = null;
if (1==maskType){
mask = new HangzhouHazeMask();
}else if (2==maskType){
mask = new HangzhouMedicalMask();
}
return mask;
}
}

此时的客户调用,可以有选择性的指定某一地区来进行下单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请选择要购买的产品产地,1:杭州,2:上海");
int location = Integer.parseInt(scanner.nextLine());
if (1 == location) {
new HangzhouMaskFactory();
} else if (2 == location) {
new ShanghaiMaskFactory();
} else {
System.out.println("暂无该地区产品");
}
}
}

模拟需求③

假设现有两种产品要进行生产:口罩和酒精,并且有杭州和上海两个工厂都可以生产这两种产品
客户可以通过自己的需求下单购买某个地址制造的某一种产品
使用代码实现这一流程

这次的需求不同以往,产品类型出现了多种,即一个工厂可以生产多种不同类型的产品,这种涉及到多个产品簇,比较推荐使用抽象工厂模式

抽象工厂模式

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

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式中一个工厂只生产一种产品,而在抽象工厂模式中,一个工厂生产多种产品,并且存在多个工厂

抽象工厂模式中有这两个概念

  • 产品等级:产品等级可以理解为同一类产品属于一个等级,比如防霾口罩、与医用外科口罩都属于口罩类,属于一个产品等级,但口罩和酒精明显不是一个产品等级
  • 产品族:同一个具体工厂所生产的位于不同产品等级的所有产品称作一个产品族。比如杭州工厂生产的杭州口罩和酒精就属于一个产品族

上面的需求用抽象工厂模式的思路得到的简单类图如下:

代码实现

其中HangzhouMask、ShanghaiMask都继承自BaseMask,HangzhouAlcohol、ShanghaiAlcohol继承自BaseAlcohol

通过定义抽象工厂接口AbstractMaskFactory,定义创建产品的方法,交由子类工厂进行实现。这里的产品创建方法可以覆盖到所有的产品等级

1
2
3
4
5

public interface AbstractFactory {
BaseMask createMask();
BaseAlcohol createAlcohol();
}
1
2
3
4
5
6
7
8
9
10
11
12
public class HangzhouFactory implements AbstractFactory{

@Override
public BaseMask createMask() {
return new HangzhouHazeMask();
}

@Override
public BaseAlcohol createAlcohol() {
return new HangzhouAlcohol();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

public class ShanghaiFactory implements AbstractFactory{

@Override
public BaseMask createMask() {
return new ShanghaiHazeMask();
}

@Override
public BaseAlcohol createAlcohol() {
return new ShanghaiAlcohol();
}
}

创建了工厂类后,客户可以通过某一工厂进行指定产品的下单操作,这些逻辑封装在了Store类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Store {
private AbstractFactory factory;

public Store(AbstractFactory factory) {
this.factory = factory;
}

public void orderMask() {
BaseMask mask = null;
mask = factory.createMask();
if (!Objects.isNull(mask)) {
mask.prepare();
mask.processing();
mask.bale();
} else {
System.out.println("不支持的产品类型...");
}
}

public void orderAlcohol() {
BaseAlcohol alcohol = null;
alcohol = factory.createAlcohol();
if (!Objects.isNull(alcohol)) {
alcohol.prepare();
alcohol.processing();
alcohol.bale();
} else {
System.out.println("不支持的产品类型...");
}
}
}

3种工厂模式的总结

本文一共提到了三种工厂模式,简单工厂模式、工厂方法模式、抽象工厂模式,也根据模拟场景对其进行了简单的说明

从上面的介绍中可以简单做下总结

  • 简单工厂模式
    • 实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责
    • 工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码
    • 而且产品较多时,工厂方法代码逻辑将会非常复杂
  • 工厂方法模式
    • 定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成
    • 系统需要新增一个产品是,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类
    • 系统的扩展性变得很好,符合面向对象编程的开闭原则
  • 抽象工厂模式
    • 工厂模式的升级版,工厂方法模式中一个工厂负责生产一类产品,而抽象工厂模式中一个工厂可以生产多种产品
    • 扩展性更强,无论是增加工厂,还是增加产品,抽象工厂模式都比工厂方法模式更为便捷

关于工厂方法模式和抽象工厂模式的几点区别如下:

  • 工厂方法模式利用继承,抽象工厂模式利用组合
  • 工厂方法模式产生一个对象,抽象工厂模式产生一族对象
  • 工厂方法模式利用子类创造对象,抽象工厂模式利用接口的实现创造对象

常见的工厂模式的运用

  • JDK中Calendar的getlnstance方法
  • JDBC中的Connection对象的获取
  • Spring中IOC容器创建管理bean对象
  • 反射中Class对象的newlnstance方法