1.工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)

  • 工厂方法模式(Factory Method)

  • 抽象工厂模式(Abstract Factory)

简单工厂其实不是一个标准的的设计模式。GOF 23 种设计模式中只有「工厂方法模式」与「抽象工厂模式」。简单工厂模式可以看为工厂方法模式的一种特例,为了统一整理学习,就都归为工厂模式。

这三种工厂模式在设计模式的分类中都属于创建型模式,三种模式从上到下逐步抽象。

2.创建型模式

创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。

创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

工厂模式是创建型模式中比较重要的。工厂模式的主要功能就是帮助我们实例化对象。之所以名字中包含工厂模式四个字,是因为对象的实例化过程是通过工厂实现的,是用工厂代替 new 操作的。

3.工厂模式优点:

  • 可以使代码结构清晰,有效地封装变化。在编程中,产品类的实例化有时候是比较复杂和多变的,通过工厂模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产品。

  • 对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。

  • 降低耦合度。产品类的实例化通常来说是很复杂的,它需要依赖很多的类,而这些类对于调用者来说根本无需知道,如果使用了工厂方法,我们需要做的仅仅是实例化好产品类,然后交给调用者使用。对调用者来说,产品所依赖的类都是透明的。

4.适用场景

不管是简单工厂模式,工厂方法模式还是抽象工厂模式,他们具有类似的特性,所以他们的适用场景也是类似的。

首先,作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

其次,工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。

再次,由于工厂模式是依靠抽象架构的,它把实例化产品的任务交由实现类完成,扩展性比较好。也就是说,当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。

一、简单工厂模式

1、简单工厂模式的角色

简单工厂模式的角色如下:

角色 解释
简单工厂SimpleFactory 负责创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品IProduct 简单工厂创建的多有对象的父类,负责描述所有实例共有的公共接口。
具体产品ConcreteProduct 简单共产模式的创建目标

2、UML 类图

3、简单工厂模式的通用写法

抽象产品类IProduct:

public interface IProuduct {void doSomeThing();
}

具体产品类ProductA:

@Slf4j
public class ProductA implements IProuduct {@Overridepublic void doSomeThing() {log.info("我是ProductA");}
}

具体产品类ProductB:

@Slf4j
public class ProductB implements IProuduct {@Overridepublic void doSomeThing() {log.info("我是ProductB");}
}

简单工厂类SimpleFactory(通过传入的productName来决定生成哪个具体产品)

public class SimpleFactory {static IProuduct makeProduct(String productName) {if ("ProductA".equals(productName)) {return new ProductA();} else if ("ProductB".equals(productName)) {return new ProductB();} else {return null;}}
}

客户端Client类:

public class Client {public static void main(String[] args) {// 生成产品BIProuduct product = SimpleFactory.makeProduct("ProductB");product.doSomeThing();}
}

在简单工厂模式中,抽象产品既可以是各个具体产品类实现的共同的接口,也可以是各个具体产品类继承的抽象父类。

4、 简单工厂模式总结

优点

简单工厂模式,封装了创建对象的逻辑,完成了创建对象逻辑与业务代码逻辑的解耦。试想客户端是多个service层的文件,对比不使用简单工厂模式,当我们要改变产生对象的逻辑时,需要在多个service文件中找到这部分代码进行修改。在使用简单工厂模式后,只需要修改简单工厂中生成对象的逻辑即可,不需要修改业务代码。完成了解耦。

缺点:

每当具体产品类的抽象产品类增多时,会需要在简单工厂类中新增关于新增产品类对象生成的方法。当抽象产品类很多时,抽象工厂会很臃肿。并且在这种情形下,SimpleFactory类也不符合开闭原则。

二、工厂方法模式

工厂方法模式:定义创建对象的接口,但具体实现放到实现这个接口的管理具体一类产品的对象生成的工厂类中去实现。

1、工厂方法模式的角色

角色 解释
抽象工厂 定义了创建抽象产品的方法,任何在模式中创建对象的工厂类都必须实现这个接口
具体工厂 实现抽象工厂接口的具体工厂类,负责生产具体的产品
抽象产品 工厂方法模式所创建的对象的超类型。也是产品对象的共同父类或者共同拥有的接口
具体产品 实现了抽象产品角色所定义的接口。某具体产品由具体工厂创建,他们往往一一对应

2、UML 类图

3、工厂方法模式的通用写法

抽象工厂:

/*** 抽象工厂*/
public interface IFactory {IProduct makeProduct();
}

抽象产品:

/*** 抽象产品*/
public interface IProduct {void doSomeThing();
}

具体产品ProductA:

/*** 具体产品A*/
@Slf4j
public class ProductA implements IProduct {@Overridepublic void doSomeThing() {log.info("我是productA");}
}

具体产品ProductB:

/*** 具体产品B*/
@Slf4j
public class ProductB implements IProduct {@Overridepublic void doSomeThing() {log.info("我是ProductB");}
}

生产ProductA的具体工厂FactoryA:

/*** 生产ProductA的具体工厂*/
public class FactoryA implements IFactory {@Overridepublic IProduct makeProduct() {return new ProductA();}
}

生产ProductB的具体工厂FactoryB:

/*** 生产ProductB的具体工厂*/
public class FactoryB implements IFactory {@Overridepublic IProduct makeProduct() {return new ProductB();}
}

客户端:

public class Client {public static void main(String[] arges) {// 生产ProductAFactoryA factoryA = new FactoryA();factoryA.makeProduct().doSomeThing();}
}

4、工厂方法模式适用场景

工厂方法模式和简单工厂模式虽然都是通过工厂来创建对象,他们之间最大的不同是——工厂方法模式在设计上完全完全符合“开闭原则”。

在以下情况下可以使用工厂方法模式:

  • 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。

  • 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

使用场景:

  • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

  • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

  • 设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

  • 比如 Hibernate 换数据库只需换方言和驱动就可以

5、工厂方法模式总结

从简单工厂模式的讲述知道:简单工厂的一个缺点在于,每当需要新增产品时,都需要修改负责生产产品的SimpleFactory类,违背了“开闭原则”,并且会使SimpleFactory类十分的臃肿。而使用工厂方法模式后,当新增ProductC时,只需要对应创建具体产品类ProductC和负责生产ProductC的具体工厂FactoryC即可。符合“开闭原则”,便于扩展。

它的缺点在于:

(1)类的个数容易过多,增加复杂度

(2)实现抽象工厂接口的具体工厂只能生产出一种产品(可以用抽象工厂模式解决)

三、抽象工厂模式

抽象工厂模式在工厂方法模式的基础上进行进一步抽象。设想下面这种场景:

现有两种具体产品(具体产品):篮球,足球(在此基础上的抽象产品可看成球)。同时,对于足球和篮球来说,他们都有两种品牌安踏、李宁。

如果采用工厂方法模式解决上述场景中创建产品的问题,需要在抽象工厂中定义创建产品的方法,并新建四个用于创建具体产品的具体工厂类:用于创建“李宁篮球”的具体工厂,用于创建“李宁足球”的具体工厂,用于创建“安踏篮球”的具体工厂,用于创建“安踏足球”的具体工厂。

从上面的解决方式可以看出:使用工厂方法模式,创建了很多具体工厂类,而没有利用产品的“商品族”的概念。

由此引出,抽象工厂模式是用于解决“一类产品”的创建问题(在上述场景中,可以把“李宁篮球”,“李宁足球”,“安踏篮球”,“安踏足球”归纳为:“篮球”,“足球”这两类商品)

1、抽象工厂模式的角色

角色 解释
抽象工厂 声明创建抽象产品对象的一个接口(有几个产品组,则声明几个方法。比如对于上述的场景,需要声明一个用于生产篮球类产品的方法,还需要声明一个用于生产足球类产品的方法)
具体工厂 实现创建具体产品对象的操作
抽象产品 为一类产品对象的抽象
具体产品 定义一个将被相应的具体工厂创建的产品对象(比如上述场景中的:李宁篮球)

2、UML 类图

3、抽象工厂模式的通用写法

抽象工厂:

/*** 抽象工厂*/
public interface AbstractFactory {Basketball makeBasketball();Football makeFootball();
}

抽象产品族篮球:

/*** 抽象产品族;篮球*/
public interface Basketball {void sayBasketball();
}

抽象产品族足球:

/*** 抽象产品族:足球*/
public interface Football {void sayFootball();
}

四个具体产品:李宁篮球、李宁足球、安踏篮球、安踏足球

/*** 具体产品:李宁篮球*/
@Slf4j
public class LiningBasketball implements Basketball {@Overridepublic void sayBasketball() {log.info("我是李宁篮球");}
}/*** 具体产品:李宁足球*/
@Slf4j
public class LiningFootball implements Football {@Overridepublic void sayFootball() {log.info("我是李宁足球");}
}/*** 具体产品:安踏篮球*/
@Slf4j
public class AntaBasketball implements Basketball {@Overridepublic void sayBasketball() {log.info("我是安踏篮球");}
}/*** 具体产品:安踏足球*/
@Slf4j
public class AntaFootball implements Football {@Overridepublic void sayFootball() {log.info("我是安踏足球");}
}

具体工厂:

/*** 具体工厂,负责生产李宁篮球,李宁足球*/
public class LiningFactoy implements AbstractFactory {@Overridepublic Basketball makeBasketball() {return new LiningBasketball();}@Overridepublic Football makeFootball() {return new LiningFootball();}
}/*** 具体工厂,负责生产安踏篮球,安踏足球*/
public class AntaFactory implements AbstractFactory {@Overridepublic Basketball makeBasketball() {return new AntaBasketball();}@Overridepublic Football makeFootball() {return new AntaFootball();}
}

客户端:

public class Client {public static void main(String[] args){// 生产李宁篮球和安踏足球LiningFactoy liningFactoy = new LiningFactoy();AntaFactory antaFactory = new AntaFactory();liningFactoy.makeBasketball().sayBasketball();antaFactory.makeFootball().sayFootball();}
}

4、抽象工厂模式适用场景

抽象工厂模式和工厂方法模式一样,都符合开闭原则。但是不同的是,工厂方法模式在增加一个具体产品的时候,都要增加对应的工厂。但是抽象工厂模式只有在新增一个类型的具体产品时才需要新增工厂。也就是说,工厂方法模式的一个工厂只能创建一个具体产品。而抽象工厂模式的一个工厂可以创建属于一类类型的多种具体产品。工厂创建产品的个数介于简单工厂模式和工厂方法模式之间。

在以下情况下可以使用抽象工厂模式:

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。

  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。

  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。

  • 系统结构稳定,不会频繁的增加对象。

“开闭原则”的倾斜性

在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为**“开闭原则”的倾斜性**。“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

  • 增加产品族:对于增加新的产品族,工厂方法模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。

  • 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。

正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。

5、抽象工厂模式总结

抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和 API 类库的设计中,例如在 Java 语言的 AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。抽象工厂模式也是在软件开发中最常用的设计模式之一。

优点:

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。

  • 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:

增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

工厂模式的退化

当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。

JAVA设计模式之工厂模式(三种工厂模式)相关推荐

  1. zmq java 消息阻塞_ZMQ的三种消息模式

    一. ZMQ是什么? 普通的socket是端对端(1:1)的关系,ZMQ是N:M的关系,socket的连接需要显式地建立连接,销毁连接,选择协议(TCP/UDP)和 错误处理,ZQM屏蔽了这些细节,像 ...

  2. STM32单片机的启动模式 三种BOOT模式介绍

    在绘制32单片机时,参考别人的原理图对单片机的启动方式BOOT有疑问, 这里写目录标题 自己的理解和应用 一.三种BOOT模式启动的介绍 二.自己开发BOOT模式的选择 官方的文档介绍 自己的理解和应 ...

  3. Flink的三种执行模式STREAMING和BATCH和AUTOMATIC

    执行模式 执行模式三种 BATCH模式的两种配置方法 什么时候选择BATCH模式

  4. linux文件编辑器的三种模式,Linux中文本编辑器三种工作模式切换及vi编辑器三种工作模式下命令详解...

    文本编辑器的作用 创建或修改文本文件 维护Linux系统中的各种配置文件 Linux中最常用的文本编辑器 Linux中最常用的文本编辑器 vi:类UNIX操作系统的默认文本编辑器 vim:vim时vi ...

  5. stm32f103c6t6下的HAL库搭建三种低功耗模式及实战分析(stm32通用)

    目录 三种低功耗模式介绍 睡眠模式(sleep mode) 停止模式(stop mode) 待机模式(standby mode) 总结 实战测试 个别电路原理图 功耗分析 ADC功耗大解决方案 ADC ...

  6. java设计模式---三种工厂模式之间的区别

    简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式.其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性. 本文是本人对这三种模式学习后的一个小结以及对他 ...

  7. 什么java工厂模式_java的三种工厂模式是什么?

    java的三种工厂模式:1.简单工厂模式,提供一个创建对象实例的功能,而无须关心其具体实现:2.工厂方法模式:3.抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,无须指定它们具体的类. 一. ...

  8. 剖析java三种工厂设计模式

    java三种工厂设计模式 1. 简单工厂模式 简单工厂有以下三个主角: Factory:工厂负责生产产品 AbstructProduct:抽象产品,就好比华为手机,我没说是华为那个型号手机. Prod ...

  9. java 三种工厂模式

    java 三种工厂模式 一.简单工厂模式 一个栗子:  我喜欢吃面条,抽象一个面条基类,(接口也可以),这是产品的抽象类. public abstract class INoodles {/*** 描 ...

最新文章

  1. HTTP API 设计指南(基础部分)
  2. 动态规划在求解硬币问题中的应用(JAVA)--币制最大化、找零问题、硬币收集问题
  3. 一个使用Jmeter做接口性能测试的实战案例
  4. 科比职业生涯数据分析
  5. python散点图获取边界_获取离散点的边界点
  6. DOA估计谱峰搜索的一种策略
  7. MYSQL 思考题5 参考答案
  8. 3无重复字符的最长子串longest-substring-without-repeating-characters
  9. java输出26个字母_Java语言:输出26个英文字母(从键盘输入)
  10. 揭秘腾讯智慧城市版图:“数字政府”+“超级大脑”的新打法
  11. 麻省理工MIT计算机课程表
  12. 抖音小程序实践二:常用权限申请
  13. Linux – TFTP服务器搭建 FTP服务器搭建
  14. 几种隐藏批处理运行窗口的方法
  15. Camtasia Studio 6录制视频时鼠标闪烁的解决办法
  16. c语言中的指数和尾数是什么意思,浮点数的指数和尾数的研究
  17. Linux 中 mv 指令中的 文件转移
  18. Vue设置背景图片(全屏背景),实际操作与踩雷.
  19. HTML和CSS整合笔记
  20. 数字证书、CA、CA证书,傻傻分不清楚?这一篇看懂!

热门文章

  1. MFC编程课程设计——飘动的气球
  2. 蚂蚁集团获新加坡金管局数字银行牌照 全球仅4家!
  3. 小白如何入门Python?记我的Python初体验
  4. 2013 五月份以前的一年半ACM算法生涯
  5. oracle RAC环境 1521端口NAT映射后,端口能通,数据库报ORA-12541
  6. “ Infinity”是默认超时错误
  7. mybatis的2种缓存机制(1)
  8. 使用 Vite 和 TypeScript 从零打造一个属于自己的 Vue3 组件库
  9. 方兴东:中国网站十年
  10. oracle 查看闪回大小,闪回区大小出现警告解决