1、什么是工厂模式

Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

说人话:提供创建对象的接口,将创建对象的过程屏蔽,从而达到灵活的目的。

2、工厂模式分类

一般情况下,工厂模式分为三类:

①、简单工厂模式(Simple Factory)

②、工厂方法模式(Factory Method)

③、抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。

需要说明的是:GOF 在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory),将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

下面我们分别介绍这三种工厂模式。

2.1 简单工厂(Simple Factory)

比如有这样一个需求:

根据导入的不同文件(docx,xlsx,pptx),选择不同的解析器进行解析。

简单工厂有三个核心对象:

1.工厂:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。

2.抽象产品 :简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

3.具体产品:是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

①、抽象解析器

public interface IOfficeParser {
void parse();
}

②、具体解析器(docx,xlsx,pptx)

public class WordParser implements IOfficeParser{
private String filePath;
public WordParser(String filePath){
this.filePath = filePath;
}
@Override
public void parse() {
System.out.println("解析 docx 文件");
}
}
public class ExcelParser implements IOfficeParser{
private String filePath;
public ExcelParser(String filePath){
this.filePath = filePath;
}
@Override
public void parse() {
System.out.println("解析 xlsx 文件");
}
}
public class PptParser implements IOfficeParser {
private String filePath;
public PptParser(String filePath){
this.filePath = filePath;
}
@Override
public void parse() {
System.out.println("解析 pptx 文件");
}
}

③、构造解析器的工厂

public class OfficeParserFactory {

    public static IOfficeParser getParser(String filePath) throws Exception {
String fileExtension = getFileExtension(filePath);
IOfficeParser parser = null;
if("docx".equalsIgnoreCase(fileExtension)){
parser = new WordParser(filePath);
}else if("xlsx".equalsIgnoreCase(fileExtension)){
parser = new ExcelParser(filePath);
}else if("pptx".equalsIgnoreCase(fileExtension)){
parser = new PptParser(filePath);
}else{
throw new Exception("file is not supported:"+fileExtension);
}
return parser;
} private static String getFileExtension(String filePath){
// 解析文件名获取文件扩展名,比如 文档.docx,返回 docx
String fileExtension = filePath.substring(filePath.lastIndexOf(".")+1);
return fileExtension;
}
}

④、测试类

public class SimpleFactoryTest {

    public static void main(String[] args) throws Exception {
String filePath = "文档.docx";
IOfficeParser parser = OfficeParserFactory.getParser(filePath);
parser.parse(); String filePath1 = "表格.xlsx";
IOfficeParser parser1 = OfficeParserFactory.getParser(filePath1);
parser1.parse();
}
}

⑤、总结

这便是简单工厂,客户端避免了直接创建解析器的责任,只需要调用工厂类去解析就行了。

可以从开闭原则(对扩展开放,对修改关闭)来分析简单工厂模式:当增加一种文件解析,比如老版本的 doc 格式。这时候只需要新增一个 parser 类即可,客户端(理解为测试类,调用端)不用改变,然后在工厂类 OfficeParserFactory 新增一个 else-if 分支即可。

这时候可能有同学会问了,那修改了 OfficeParserFactory 类,不就违反开闭原则了吗?但其实只要不是频繁的添加新的 parser,偶尔修改一下 OfficeParserFactory 类,稍微不符合开闭原则,也是可以接受的。

看上去比较完美,细心的同学可能会问,所有的解析类对象创建都在 OfficeParserFactory 类中,假设某个解析类,比如 doc 创建parser 对象并不是简单的 new ,还包括一些其它的操作,这时候难道把这些代码也全部写到 OfficeParserFactory 中吗?有没有更优雅的写法呢?

有,就是下面要介绍的 工厂模式。

2.2 工厂方法(Factory Method)

为了解决上面的问题,我们可以为工厂类在创建一个工厂,也就是工厂的工厂,用来创建工厂类对象。

①、给每一个具体解析器创建工厂

public class ExcelParserFactory implements IOfficeParserFactory {

    @Override
public IOfficeParser createParser() {
// TODO 进行创建对象的一些操作
return new ExcelParser();
}
}

②、创建解析器的工厂

public class OfficeParserFactory {

    public static IOfficeParser getParser(String filePath) throws Exception {
String fileExtension = getFileExtension(filePath);
IOfficeParserFactory parserFactory = OfficeParserFactoryMap.getOfficeParseFactory(fileExtension);
if(parserFactory == null){
throw new Exception("file is not supported:"+fileExtension);
}
IOfficeParser parser = parserFactory.createParser();
return parser;
} private static String getFileExtension(String filePath){
// 解析文件名获取文件扩展名,比如 文档.docx,返回 docx
String fileExtension = filePath.substring(filePath.lastIndexOf(".")+1);
return fileExtension;
}
}

③、创建解析器工厂的工厂类

public class OfficeParserFactoryMap {
private static final Map<String, IOfficeParserFactory> parserFactoryCached = new HashMap<>();
static {
parserFactoryCached.put("docx",new WordParserFactory());
parserFactoryCached.put("xlxs",new ExcelParserFactory());
parserFactoryCached.put("pptx",new PptParserFactory());
}
public static IOfficeParserFactory getOfficeParseFactory(String type){
if(type == null || type.isEmpty()){
return null;
}
return parserFactoryCached.get(type.toLowerCase());
} }

④、测试类

public class FactoryTest {
public static void main(String[] args) throws Exception {
String filePath = "文档.docx";
IOfficeParser parser = OfficeParserFactory.getParser(filePath);
parser.parse();
}
}

⑤、总结

在工厂模式中,如果我们要增加新的文件解析,比如 mdb 格式(office access套件),就只需要创建新的 parser 类和 parserFactory 类,并且在 OfficeParserFactoryMap 类中将新的 parserFactory 类添加到 map 中即可。代码的改动非常少,基本上是符合开闭原则的。

但是,我们看到工厂模式新增了很多 factory 类,会增加代码的复制性,如果每个 factory 类只是做简单的 new 操作,则没必要使用该模式,直接用简单工厂模式即可。

2.3 抽象工厂(Abstract Factory)

这种模式比较特殊,使用场景不多,大家简单了解一下就行。

我们知道 doc 和 docx 都是 office word 文档后缀,类似 xls 和 xlsx 都是 office Excel 表格后缀,还有 ppt 和 pptx。doc/xlx/ppt 都是旧版本 office 文件后缀,都是二进制组成,解析的时候有共同之处,而 docx/xlsx/pptx 是office新版本文件后缀,是通过 ooxml 结构组成。相当于一组是老的office,一组是新的office。

如果我们还是用工厂模式来实现的话,那每一种都要编写一个工厂类,过多的类会难以维护,那怎么解决呢?

抽象工厂模式就是针对这种特殊的场景诞生,我们可以让一个工厂复制创建多个不同类型的对象,而不是只创建一个 parser 对象。

具体代码实现如下:

public interface IOfficeParserFactory {

    IOfficeParser createParser();

    IOldOfficeParser createOldParser();
}
public class ExcelParserFactory implements IOfficeParserFactory {

    @Override
public IOfficeParser createParser() {
return new ExcelParser();
} @Override
public IOldOfficeParser createOldParser() {
return new DocParser();
}
}

3、简单工厂和工厂方法区别

简单工厂:将创建不同对象的逻辑放在一个工厂类中。

工厂方法:将创建不同对象的逻辑放在不同工厂类中,先用一个工厂类的工厂类得到某个工厂,在某这个工厂来创建对象。

这样讲区别就很明显了,如果创建对象的逻辑比较复杂,要做各种初始化操作,这时候使用工厂方法,能够将复杂的创建逻辑拆分到多个工厂类中;而创建对象的逻辑很简单,就没必要额外创建多个工厂类,直接使用简单工厂即可。

4、工厂模式的作用

封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。

代码复用:创建代码抽离到独立的工厂类之后可以复用。

隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。

控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。

看完知道为啥没事别用工厂模式了吧,因为太好用了,你会爱上它的。

Java设计模式之(二)——工厂模式的更多相关文章

  1. Java设计模式之二 ----- 工厂模式

    在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模式,又 ...

  2. Java设计模式之二工厂模式

    在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模式,又 ...

  3. Java设计模式之【工厂模式】(简单工厂模式,工厂方法模式,抽象工厂模式)

    Java设计模式之[工厂模式](简单工厂模式,工厂方法模式,抽象工厂模式) 工厂模式出现的原因 在java中,创建一个对象最简单的方法就是使用new关键字.但在一些复杂的业务逻辑中,创建一个对象不只需 ...

  4. Java 设计模式之抽象工厂模式(三)

    原文地址:Java 设计模式之抽象工厂模式(三) 博客地址:http://www.extlight.com 一.前言 上篇文章 <Java 设计模式之工厂模式(二)>,介绍了简单工厂模式和 ...

  5. Java进阶篇设计模式之二 ----- 工厂模式

    前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...

  6. Java设计模式系列-抽象工厂模式

    原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755412.html 一.概述 抽象工厂模式是对工厂方法模式的再升级,但是二者面对的场景稍显差别. ...

  7. java设计模式---三种工厂模式

    工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory).GOF在 ...

  8. java设计模式之抽象工厂模式

    上一篇文章(http://www.cnblogs.com/liaoweipeng/p/5768197.html)讲了简单工厂模式,但是简单工厂模式存在一定的问题,如果想要拓展程序,必须对工厂类进行修改 ...

  9. (1)java设计模式之简单工厂模式

    一:简单工厂模式的优点          --->在阎宏博士的<JAVA与模式>一书中开头是这样描述简单工厂模式的:简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Fa ...

  10. Java设计模式系列之工厂模式

    工厂模式将大量有共同接口的类实例化,工厂模式可以实现动态决定实例化哪一个类的对象,工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factory):添加某一种类型的 ...

随机推荐

  1. MySQL MVCC原理深入探索

    一.MVCC的由来 二.MVCC的实际应用 RR级别场景 RC级别场景 三.MVCC的实现 3.1 多版本的数据从哪里来--Undo Log 3.1.1 插入操作对应的undo log 3.1.2 删 ...

  2. UE4蓝图AI角色制作(三)

    接上一节 6. 寻路网格体代理 通过允许配置多个"代理",虚幻引擎使得用户能够轻松为大小各异的AI创建寻路网格体.首先,选中世界大纲视图中的"RecastNavMesh& ...

  3. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力

    ​ 飞桨 (PaddlePaddle) 以百度多年的深度学习技术研究和业务应用为基础,是中国首个自主研发.功能完备. 开源开放的产业级深度学习平台,集深度学习核心训练和推理框架.基础模型库.端到端开发 ...

  4. 题解 Weak in the Middle

    题目传送门 Description 有一个长度为 \(n\) 的序列 \(a_{1,2,...,n}\) ,每次可以删掉 \(a_i\),当 \(\min(a_{i-1},a_{i+1})>a_ ...

  5. 最新.NET MAUI有什么惊喜?

    .NET 6 Preview 7 现已发布啦,我们为 .NET 多平台应用程序 UI (MAUI) 引入了所有的新布局.这是性能和可靠性的重大变化.我们很高兴我们还增加了一些关于accessibili ...

  6. C++ IDE或编辑器安装

    IDE介绍 上节课我们讲了C++编译器,可是没有好的编辑器,只用记事本打代码,这谁受得了.Linux vim至少还有代码高亮(即我作文里经常会出现的"彩色的代码"),记事本连高亮都 ...

  7. Nginx安装及核心配置解析

    安装 使用yum进行安装 yum install -y nginx 查看nginx的安装位置 whereis nginx 启动测试 nginx 核心配置文件结构 读取Nginx自带的Nginx配置文件 ...

  8. 微信小程序中路由跳转

    一.是什么 微信小程序拥有web网页和Application共同的特征,我们的页面都不是孤立存在的,而是通过和其他页面进行交互,来共同完成系统的功能 在微信小程序中,每个页面可以看成是一个pageMo ...

  9. CentOS 文本编辑器

    目录 1.Nano 1.1.基础命令 1.2.快捷操作 1.3.配置文件 2.Vim 2.1.四大模式 2.2.基础命令 2.3.标准操作 2.4.高级操作 2.5.配置文件 Linux 终端的文本编 ...

  10. 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef

    基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...