前言

《设计模式自习室》系列,顾名思义,本系列文章带你温习常见的设计模式。主要内容有:

  • 该模式的介绍,包括:

    • 引子、意图(大白话解释)
    • 类图、时序图(理论规范)
  • 该模式的代码示例:熟悉该模式的代码长什么样子
  • 该模式的优缺点:模式不是万金油,不可以滥用模式
  • 该模式的实际使用案例:了解它在哪些重要的源码中被使用

该系列会逐步更新于我的博客和公众号(博客见文章底部)

也希望各位观众老爷能够关注我的个人公众号:后端技术漫谈,不会错过精彩好看的文章。

系列文章回顾

  • 【设计模式自习室】开篇:为什么我们要用设计模式?
  • 【设计模式自习室】建造者模式
  • 【设计模式自习室】原型模式
  • 【设计模式自习室】透彻理解单例模式

创建型——简单工厂/工厂模式/抽象工厂

引子

工厂模式是一个非常重要的创建型模式,但是工厂模式又分为好多种,并且网上文章很多,很多对工厂模式的定义都不是很明确,甚至还互相冲突,本文希望通过放在一起串讲的形式,力求能够用最简洁的语言理清工厂模式。

先看一个工厂模式的定义:

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。)

使用了工厂模式,我们可以将对象的创建和使用分离。用来防止用来实例化一个类的数据和代码在多个类中到处都是。

工厂模式最主要的形式是以下三种:

  • 简单/静态工厂(Simple Factory)
  • 工厂方法(Factory Method)
  • 抽象工厂(Abstract Factory)

意图

1. 简单/静态工厂(Simple Factory)

先来看简单工厂模式,它指的是,在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

在简单工厂模式中,可以根据参数的不同返回不同类的实例。

2. 工厂方法(Factory Method)

工厂方法又可以称为:

  • 工厂模式
  • 虚拟构造器(Virtual Constructor)模式
  • 多态工厂(Polymorphic Factory)模式

工厂模式通过工厂子类来确定究竟应该实例化哪一个具体产品类。不再设计一个工厂类来统一负责所有产品的创建,而是将具体产品的创建过程交给专门的工厂子类去完成。

这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性,更加符合“开闭原则”。

3. 抽象工厂(Abstract Factory)

在了解抽象工厂之前,我们先要了解下什么是产品等级结构和产品族

产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。

如果看到这里还是对抽象工厂理解不够,不要着急,下方的代码示例会给你加深理解。

类图

如果看不懂UML类图,可以先粗略浏览下该图,想深入了解的话,可以继续谷歌,深入学习:

1. 简单/静态工厂(Simple Factory)

简单工厂模式包含如下角色:

  • Factory:工厂角色 负责实现创建所有实例的内部逻辑
  • Product:抽象产品角色 是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
  • ConcreteProduct:具体产品角色 是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

2. 工厂方法(Factory Method)

(相比简单工厂,将工厂变为了抽象工厂和具体工厂)

  • Factory:抽象工厂,担任这个角色的是工厂方法模式的核心,任何在模式中创建对象的工厂类必须实现这个接口。在实际的系统中,这个角色也常常使用抽象类实现。
  • ConcreteFactory:具体工厂,担任这个角色的是实现了抽象工厂接口的具体Java类。具体工厂角色含有与业务密切相关的逻辑,并且受到使用者的调用以创建具体产品对象。
  • Product:抽象产品,工厂方法模式所创建的对象的超类,也就是所有产品类的共同父类或共同拥有的接口。在实际的系统中,这个角色也常常使用抽象类实现。
  • ConcreteProduct:具体产品,这个角色实现了抽象产品(Product)所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品的实例。

3. 抽象工厂(Abstract Factory)

抽象工厂模式包含如下角色:

  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • ConcreteProduct:具体产品

你会发现工厂模式和抽象工厂的角色是相同的。

时序图

时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。

我们可以大致浏览下时序图,如果感兴趣的小伙伴可以去深究一下:

1. 简单/静态工厂(Simple Factory)

2. 工厂方法(Factory Method)

3. 抽象工厂(Abstract Factory)

代码样例

给出的代码中,每个类都以角色来区分

1. 简单/静态工厂(Simple Factory)

代码来自:

https://www.jianshu.com/p/d1b6731c1c0e

工厂——LoginManager
public class LoginManager {public static Login factory(String type){if(type.equals("password")){return new PasswordLogin();}else if(type.equals("passcode")){return new DomainLogin();}else{/*** 这里抛出一个自定义异常会更恰当*/throw new RuntimeException("没有找到登录类型");}}
}
抽象产品——Login接口
public interface Login {//登录验证public boolean verify(String name , String password);
}
具体产品——PasswordLogin
public class PasswordLogin implements Login {@Overridepublic boolean verify(String name, String password) {// TODO Auto-generated method stub/*** 业务逻辑*/return true;}
}

客户端调用

public class Test {public static void main(String[] args) {String loginType = "password";String name = "name";String password = "password";Login login = LoginManager.factory(loginType);boolean bool = login.verify(name, password);if (bool) {/*** 业务逻辑*/} else {/*** 业务逻辑*/}}
}

假如不使用上面的简单工厂模式则验证登录Servlet代码如下,可以看到代码耦合度很高:

public class Test {public static void main(String[] args) {// TODO Auto-generated method stubString loginType = "password";String name = "name";String password = "password";//处理口令认证if(loginType.equals("password")){PasswordLogin passwordLogin = new PasswordLogin();boolean bool = passwordLogin.verify(name, password);if (bool) {/*** 业务逻辑*/} else {/*** 业务逻辑*/}}//处理域认证else if(loginType.equals("passcode")){DomainLogin domainLogin = new DomainLogin();boolean bool = domainLogin.verify(name, password);if (bool) {/*** 业务逻辑*/} else {/*** 业务逻辑*/}    }else{/*** 业务逻辑*/}}
}

2. 工厂方法(Factory Method)

代码来自:https://www.jianshu.com/p/1cf9859e0f7c

(相比简单工厂,将工厂变为了抽象工厂和具体工厂)

抽象的产品接口——ILight:具备开关两种功能的产品

public interface ILight{void TurnOn();void TurnOff();}

具体的产品类——BulbLight

 public class TubeLight implements ILight{public void TurnOn(){Console.WriteLine("TubeLight turns on.");}public void TurnOff(){Console.WriteLine("TubeLight turns off.");}}

抽象的工厂类——ICreator

public interface ICreator{ILight CreateLight();}

具体的工厂类——BulbCreator

 public class BulbCreator implements ICreator{public ILight CreateLight(){return new BulbLight();}}

客户端调用

static void Main(string[] args){//先给我来个灯泡ICreator creator = new BulbCreator();ILight light = creator.CreateLight();light.TurnOn();light.TurnOff();//再来个灯管看看creator = new TubeCreator();light = creator.CreateLight();light.TurnOn();light.TurnOff();}

本例中每个具体工厂类只负责生产一种类型的产品,当然每个具体工厂类也内部可以维护少数几种产品实例对象,类似于简单工厂模式。

3. 抽象工厂(Abstract Factory)

代码来自:https://www.jianshu.com/p/d6622f3e71ed

抽象产品: 苹果系列

public interface Apple{void AppleStyle();}

抽象产品: 三星系列

public interface Sumsung{void BangziStyle();}

具体产品:iphone

public class iphone implements Apple{public void AppleStyle(){Console.WriteLine("Apple's style: iPhone!");}}

具体产品:ipad

 public class ipad implements Apple{public void AppleStyle(){Console.WriteLine("Apple's style: iPad!");}}

具体产品:note2

public class note2 implements Sumsung{public void BangziStyle(){Console.WriteLine("Bangzi's style : Note2!");}}

抽象工厂

public interface Factory{Apple createAppleProduct();Sumsung createSumsungProduct();}

手机工厂

public class Factory_Phone implements Factory{public Apple createAppleProduct(){return new iphone();}public Sumsung createSumsungProduct(){return new note2();}}

pad工厂

public class Factory_Pad implements  Factory{public Apple createAppleProduct(){return new ipad();}public Sumsung createSumsungProduct(){return new Tabs();}}

客户端调用

public static void Main(string[] args){//采购商要一台iPad和一台TabFactory factory = new Factory_Pad();Apple apple = factory.createAppleProduct();apple.AppleStyle();Sumsung sumsung = factory.createSumsungProduct();sumsung.BangziStyle();//采购商又要一台iPhone和一台Note2factory = new Factory_Phone();apple = factory.createAppleProduct();apple.AppleStyle();sumsung = factory.createSumsungProduct();sumsung.BangziStyle();}

下面的代码是刚才已经看过的工厂模式的调用代码,对比下,发现区别了吗?工厂方法只需要管是哪个产品族,而抽象工厂还要考虑产品的等级结构,也就是他是苹果造的,还是三星造的。尽管他们都是手机!

static void Main(string[] args){//先给我来个灯泡ICreator creator = new BulbCreator();ILight light = creator.CreateLight();light.TurnOn();light.TurnOff();//再来个灯管看看creator = new TubeCreator();light = creator.CreateLight();light.TurnOn();light.TurnOff();}

用意图里面的话再次总结一下:

工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

优缺点

1. 简单/静态工厂(Simple Factory)

优点
  • 构造容易,逻辑简单。
缺点
  • 简单工厂模式中的if else判断非常多,完全是Hard Code,如果有一个新产品要加进来,就要同时添加一个新产品类,并且必须修改工厂类,再加入一个 else if 分支才可以, 这样就违背了 “开放-关闭原则“中的对修改关闭的准则了。
  • 一个工厂类中集合了所有的类的实例创建逻辑,违反了高内聚的责任分配原则,将全部的创建逻辑都集中到了一个工厂类当中,所有的业务逻辑都在这个工厂类中实现。什么时候它不能工作了,整个系统都会受到影响。因此一般只在很简单的情况下应用,比如当工厂类负责创建的对象比较少时。
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

2. 工厂方法(Factory Method)

优点
  • 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名
  • 工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类
  • 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”,这点比简单工厂模式更优秀。
缺点
  • 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

3. 抽象工厂(Abstract Factory)

优点
  • 应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
  • 增加新的具体工厂和产品族很方便,因为一个具体的工厂实现代表的是一个产品族,无须修改已有系统,符合“开闭原则”。
缺点

开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦

使用场景举例

1. 简单/静态工厂(Simple Factory)

工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

Java JDK中使用
  1. JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale
locale);
  1. Java加密技术
    获取不同加密算法的密钥生成器:
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
  1. 创建密码器:
Cipher cp = Cipher.getInstance("DESede");

2. 工厂方法(Factory Method)

Java JDK中使用
  • JDBC中的工厂方法:
Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");
  • java.util.Calendar
  • java.util.ResourceBundle
  • java.text.NumberFormat
  • java.nio.charset.Charset
  • java.net.URLStreamHandlerFactory
  • java.util.EnumSet
  • javax.xml.bind.JAXBContext

3. 抽象工厂(Abstract Factory)

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

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。(与工厂方法模式的区别)
  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
  • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
Java JDK中使用
  • javax.xml.parsers.DocumentBuilderFactory
  • javax.xml.transform.TransformerFactory
  • javax.xml.xpath.XPathFactory

总结

抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类,抽象工厂模式较好的实现了“开放-封闭”原则,是三个模式中较为抽象,并具一般性的模式。

但是归根结底,工厂模式还是一定程度上增加了代码复杂度,有没有一种办法,不需要创建工厂,也能解决代码以后的扩展性问题呢?

答案是有的,通过控制反转(ioc),对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。(DI),也就是Spring最核心的思想了。大家可以自行查阅Spring思想的文章。

话说,最近也真的想总结一下Spring源码相关的知识,正在学习中。

参考

  • 《HEAD FIRST 设计模式》
  • https://www.jianshu.com/p/d1b6731c1c0e
  • https://www.jianshu.com/p/1cf9859e0f7c
  • https://www.jianshu.com/p/d6622f3e71ed
  • https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/abstract_factory.html

关注我

我是一名后端开发工程师。

主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向,欢迎交流。

各大平台都可以找到我

  • 微信公众号:后端技术漫谈
  • Github:@qqxx6661
  • CSDN:@Rude3Knife
  • 知乎:@Zhendong
  • 简书:@蛮三刀把刀
  • 掘金:@蛮三刀把刀

原创博客主要内容

  • Java面试知识点复习全手册
  • 设计模式/数据结构 自习室
  • Leetcode/剑指offer 算法题解析
  • SpringBoot/SpringCloud菜鸟入门实战系列
  • 爬虫相关技术文章
  • 后端开发相关技术文章
  • 逸闻趣事/好书分享/个人兴趣

个人公众号:后端技术漫谈

如果文章对你有帮助,不妨收藏,投币,转发,在看起来~

【设计模式自习室】理解工厂模式的三种形式相关推荐

  1. 设计模式之四(抽象工厂模式第三回合)

    原文:设计模式之四(抽象工厂模式第三回合) 前言 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 抽象工厂模式最大的好处便是易于交换产品系列,由于具体工厂类,例如I ...

  2. JAVA设计模式之工厂模式(三种工厂模式)

    1.工厂模式可以分为三类: 简单工厂模式(Simple Factory) 工厂方法模式(Factory Method) 抽象工厂模式(Abstract Factory) 简单工厂其实不是一个标准的的设 ...

  3. 设计模式--工厂模式(三种)

    工厂模式 概述 简单工厂 1.基本结构 2.示例 3.优缺点 4.简单工厂+配置文件解除耦合 工厂方法模式 1.概念 2.基本结构 3.示例 4.优缺点 抽象工厂模式 1.概念 2.基本结构 3.示例 ...

  4. 为什么要用工厂模式以及三种工厂模式的联系

    首先声明,本篇博文是博主自己对工厂模式的理解,本篇博文旨在通过生活来讲述,代码量会相对较少(其实没有),尽量通过生活现象去讲述缘由,这样会比较通俗易懂,如果能对大家有启发作用的话,博主深感荣幸. 首先 ...

  5. .net 有哪些主流的设计模式_「设计模式自习室」门面模式 Facade Pattern

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  6. 【设计模式自习室】门面模式 Facade Pattern

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  7. 【设计模式自习室】桥接模式 Bridge Pattern:处理多维度变化

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  8. php的类图怎么生成_PHP设计模式之简单工厂模式

    PHP设计模式之简单工厂模式 先从简单工厂入门,不管是面试还是被他人面试,在问到设计模式的时候,大多数人都会提到工厂模式.毫无疑问,工厂相关的几种模式在设计模式中是最出名的也是应用比较广泛的一种模式. ...

  9. 秒懂设计模式之抽象工厂模式(Abstract Factory Pattern)

    [版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/86644481 出自:shusheng007 ...

最新文章

  1. Django博客系统(首页文章数据展示)
  2. MyBatis一对多双向关联——MyBatis学习笔记之七
  3. 织梦的网站地图怎么做html,如何优化织梦dedecms默认网站地图sitemap.html??
  4. vm_comcat报错 - 数字或值错误 : 字符串缓冲区太小
  5. Node.js与Sails~Model和ORM的持久化
  6. jqGrid格式化日期
  7. TikZ绘图示例——尺规作图:任意等分半圆弧
  8. C++ Qt全局异常处理器_异常处理
  9. 【论文笔记】NLP 预训练模型综述
  10. python加粗字体_Python:openpyxl将字体更改为粗体 - python
  11. 100个Python实战项目(七)实现摩斯密码翻译器
  12. 【DSP】DSP2833x.h中定义的指令EINT、DINT、ERTM、DRTM、EALLOW、EDIS、ESTOP0的含义
  13. Postman中tests的基本使用
  14. Servlet的三个名字
  15. 【SSM框架】MyBatis
  16. 标题、首图、详情页:三位一体的搜索优化技巧
  17. PG的timestamp
  18. 【新手向】Python做一个简易登录注册GUI界面(无事件绑定)
  19. android橡皮擦擦图片功能
  20. python公里转英里_python公里转英里_从英里换算为公里

热门文章

  1. python绘制训练结果曲线图和散点图、解决坐标刻度标签重复问题 、利用训练标准输出流绘制
  2. “而立”,“不惑”和“知天命”--程序员的三个层次
  3. SQL 存储过程 procedure 讲解+代码实例
  4. pl sql developer中的CnPlugin插件,sql代码自动补全提示,批量添加单引号
  5. element table 暂无数据
  6. ProxmoxVE-让你的老旧Server换发第二春
  7. HIVE常用命令——使用SHELL命令和HDFS命令
  8. percona mysql 审计_Percona MySQL Audit to rsyslog
  9. 南阳理工学院计算机二级成绩查询,南阳理工学院2012年上半年全国计算机等级考试报名通知...
  10. 百度小程序页面基础信息配置文档