设计模式之工厂方法模式(Factory Method Pattern)
一、工厂方法模式的诞生
在读这篇文章之前,我先推荐大家读《设计模式之简单工厂模式(Simple Factory Pattern)》这篇文档。工厂方法模式是针对简单工厂模式中违反开闭原则的不足,而提出的改进方案。在简单工厂模式中,每增加一个新产品,就要修改工厂类的调度方法, 针对这个问题,工厂方法模式提出,工厂类提取成抽象的类或接口,定义调度规范。每新增一种产品,不仅要提供产品的实现类,还要提供调度这种产品的工厂类。这样,用户在用工厂方法模式时,需要哪个产品,就调用哪个产品的工厂类,即可获得。就不用只通过一个工厂类传参,去获得不同产品了。简单工厂模式改变的是产品参数,工厂方法模式改变的是工厂类型。
二、工厂方法模式的定义
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。它的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
通过第一部分的描述,这些官方的定义概念,是不是很好理解了呢?
二、工厂方法模式的角色
由上面的描述,我们可以得到工厂方法中,设计到的角色:
- 产品规范:和简单工厂模式的产品规范作用一样,定义规范;
- 产品实现:和简单工厂模式的产品实现一样,实现产品规范的方法;
- 抽象工厂:工厂方法模式新加的角色,用来制定调度产品的规范,供具体工厂类遵循;
- 工厂实现类:实现抽象工厂的规范,来调度不同的产品;不同的产品有不同的工厂实现来调度;
- 产品使用者:也就是业务程序员,工厂方法模式的使用,主要是选择不同的工厂实现类,来获取不同的产品;
三、工厂方法模式的实现
首先,还以家具为例,先定义产品规范,如下:
/**
* 产品规范
*/
public interface Furiniture {
public void createFurniture();
}
然后,定义产品实现,如下:
/**
* 产品实现类
*/
public class Table implements Furiniture {
@Override
public void createFurniture() {
System.out.println("生产桌子");
}
}
/**
* 产品实现类
*/
public class Chair implements Furiniture {
@Override
public void createFurniture() {
System.out.println("生产椅子");
}
}
下面,我们要定义工厂,在这里,我们工厂也定义工厂抽象规范,让其他具体工厂类进行实现,如下:
/**
* 工厂抽象类,定义调度产品的规范
*/
public abstract class FurnitureFactory {
public abstract void getFurniture();
}
然后定义工厂的实现类,因为现在有Table和Chair两个产品,所以需要两个工厂实现类,如下:
/**
* 桌子产品的工厂类
*/
public class TableFactory extends FurnitureFactory {
@Override
public void getFurniture() {
new Table().createFurniture();
}
}
/**
* 椅子工厂的工厂类
*/
public class ChairFactory extends FurnitureFactory {
@Override
public void getFurniture() {
new Chair().createFurniture();
}
}
调用者使用工厂方法模式,获得产品:
public class FactoryMethodUser {
public static void main(String[] args) {
//对于使用者而言,要选择具体的产品工厂类,进行产品的获得
FurnitureFactory factory=new TableFactory();
factory.getFurniture();
}
}
当我们想增加产品时,如增加Sofa产品,需要增加产品实现类和产品工厂实现类。
四、工厂方法模式的优缺点
优点:
1.面向接口编程;
2.符合了开闭原则;
缺点:
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度。
五、与简单工厂模式的区别
工厂方法模式与简单工厂模式的区别主要是两方面:
1.对于产品提供方来说,不光要提供新的产品实现,还要提供产品的调度工厂实现类,需要提供两套规范的东西;
2.对于使用工厂模式的人来说,获得产品,不再是通过一个工厂,通过传入不同的产品参数来获得产品,而是直接选择相应的工厂实现类来获得产品即可。即简单工厂是一个工厂输出不同产品,工厂方法模式是不同工厂输出不同产品。
思考:工厂方法模式中,抽象工厂类,定义了统一规范,那产品是否有必要再定统一接口规范呢?
有必要。在定义抽象工厂的规范时,我们是要返回统一产品的。如何统一产品呢?就是定义产品规范。如果抽象工厂里不统一产品输出,那调用者调用起来,也就会产生五花八门的产品,这样明显是不合理的。
六、工厂方法模式在JDK中的应用
我们以Collection集合为例,说明工厂方法模式的应用。
Collection中用到的工厂方法模式,其实是工厂方法模式的变种,不是标准的工厂方法模式。我们来分析一下。
首先,找产品规范,是JDK定义的迭代器接口Iterable, 也就是实现产品,要继承Iterable接口,实现iterator()方法。核心代码如下图:

工厂抽象类:Collection接口,该抽象工厂定义了生产集合的规范。我们使用的ArrayList,HashSet,就是抽象工厂的实现类。
我们看Collection的源码可以发现,其继承了Iterable接口。也就是说,抽象工厂中,不光有工厂的规范,还有产品的规范。也就是说,在Collection中,将工厂规范和产品规范合二为一了。
那么我们可以猜想,工厂的实现类,和产品的实现类,也合二为一了。那么进一步推想,工厂类其实就是产品实现类了,那我们还用写调度方法吗,肯定不需要。我们只需要调用产品的方法,即可。
这样设计,就省去了工厂类调度产品,再通过产品调用产品方法的逻辑。直接通过工厂类,调用产品的方法。
我们看ArrayList的源码,其实现思路也是按上面的分析来实现的。
首先,ArrayList中无需写调度方法了,我们直接找其产品方法,iterator()方法。

由源代码可以看出,产品规范实现返回了Itr对象。来实现接口规范。那我们在调用时,直接通过ArrayList对象的iterator()就可以使用该产品了。
Collection实现工厂方法模式比较隐蔽,不容易被人发现。
设计模式之工厂方法模式(Factory Method Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern)
原文:乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pa ...
- 设计模式-03工厂方法模式(Factory Method Pattern)
插曲.简单工厂模式(Simple Factory Pattern) 介绍工厂方法模式之前,先来做一个铺垫,了解一下简单工厂模式,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背 ...
- 【设计模式】工厂方法模式 Factory Method Pattern
在简单工厂模式中产品的创建统一在工厂类的静态工厂方法中创建,体现了面形对象的封装性,客户程序不需要知道产品产生的细节,也体现了面向对象的单一职责原则(SRP),这样在产品很少的情况下使用起来还是很方便 ...
- 二十四种设计模式:工厂方法模式(Factory Method Pattern)
工厂方法模式(Factory Method Pattern) 介绍定义一个用于创建对象的接口,让子类决定将哪一个类实例化.Factory Method使一个类的实例化延迟到其子类. 示例有SqlMes ...
- 【UE4 设计模式】工厂方法模式 Factory Method Pattern 及自定义创建资源
概述 描述 又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式,或者多态工厂(Polymorphic Factory)模式 工厂父类负责定义创建产品对象的公共接口,而工厂子类 ...
- 设计模式之 - 工厂方法模式 (Factory Method design pattern)
1. 模式意图: 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类. 2. 别名(Virtual Constructor) 3. 结构 4. 工厂方法模式C ...
- C#设计模式——工厂方法模式(Factory Method Pattern)
一.概述在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?如何提供一种封装机制来隔离出“这个易变对象 ...
- 六个创建模式之工厂方法模式(Factory Method Pattern)
问题: 在使用简单工厂模式的时候,如果添加新的产品类,则必需修改工厂类,违反了开闭原则. 定义: 定义一个用于创建对象的接口,让子类决定具体实例化哪个产品类.此时工厂和产品都具有相同的继承结构,抽象产 ...
- 工厂方法模式(Factory Method Pattern)
工厂方法模式概述 工厂方法模式是为了弥补简单工厂模式的不足并且继承它的优点而延生出的一种设计模式,属于GoF中的一种.它能更好的符合开闭原则的要求. 定义:定义了一个用于创建对象的接口,但是让子类决定 ...
随机推荐
- 牛客小白月赛17 A 小sun的假期
传送门 题意: 第一行两个数n,m,代表总共有n天,m个安排.接下来有m行,每行是一个安排l,r,代表从第l天到第r天,小sun有安排了.安排可能会重复. 小 sun 非常喜欢放假,尤其是那种连在一起 ...
- Codeforces Round #481 (Div. 3) C. Letters (模拟,二分)
题意:有个\(n\)个公寓,每个公寓\(a_{i}\)代表着编号为\(1-a_{i}\)个房间,给你房间号,问它在第几栋公寓的第几个房间. 题解:对每个公寓的房间号记一个前缀和,二分查找属于第几个公寓 ...
- Jpress小程序
首页轮播.首页公告.首页宫格.个人中心页面均支持在PC后台设置内容 首页列表.分类列表页.搜索列表的文章展示页均支持后台设置,拥有三种风格 所有分类展示支持两种风格 用户中心授权登陆,查看个人数据 J ...
- Java之大数相加
之前参加某公司笔试,机试题目是大数相加,两大数是字符串形式,求和. 当时讨巧用的是BigDecimal类,但是发迷糊了,以为b1.add(b2)后,和就加到b1上了,结果一直输出不对. 其实应该是这样 ...
- axios增加自定义headers,页面上出现,服务端收不到
问题 axios增加自定义headers,页面上出现,服务端收不到 原因 vue-cli起的服务是用node-http-proxy中间件处理的 默认是只有几个常用的header,自定义header是直 ...
- KafkaBroker 简析
Kafka 依赖 Zookeeper 来维护集群成员的信息: Kafka 使用 Zookeeper 的临时节点来选举 controller Zookeeper 在 broker 加入集群或退出集群时通 ...
- OAuth 2.0 All In One
OAuth 2.0 All In One 授权类型 授权代码 隐式 密码凭证 客户端凭证 授权码 授权码授予类型要求用户向提供者进行身份验证-然后将授权码发送回客户端应用程序,提取并与提供者交换以获取 ...
- TypeScript & LeetCode
TypeScript & LeetCode TypeScript In Action TypeScript 复杂类型 编写复杂的 TypeScript 类型 // 方法「只可能」有两种类型签名 ...
- css animation & animation-fill-mode
css animation & animation-fill-mode css animation effect https://developer.mozilla.org/en-US/doc ...
- taro 渲染 html
taro 渲染 html https://taro-docs.jd.com/taro/next/docs/next/html.html // Taro 更推荐使用框架自带的渲染 HTML 方式 // ...