定义
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
该定义是对生产者一方的描述,涉及四种角色,包括接口、接口实现类、被实例化的类、抽象产品(隐含)。其中
-
接口是指工厂接口(广义接口,在java中可以是接口或者抽象类),该接口定义一个生产产品的方法;
-
接口实现类指工厂接口的实现子类;
-
实例化的类指生产的产品;
-
抽象产品指产品的父类。工厂接口中要先给出抽象产品,其子类才能实例化具体的产品;
定义中第二句话实际上给出了工厂方法的功能,即让子类决定要实例化的类是哪一个。此处的“决定”并不是指运行时的决定,而是在编码过程中程序员的决定。工厂子类和产品实例的对应关系在编码时已经确定,执行时选择哪个工厂实例就可以生产对应的产品。举个例子,工厂子类和产品实例的关系好比是火车和目的地的关系,选择了开往北京的火车,那么最终的目的地就是北京,不会是其他地方。
注意,该模式的核心是工厂方法,实际上这个方法是一个抽象方法,用于定义创建产品的规范。
模式结构
从定义可知,除客户端外,该模式包含了四种角色。从两个平行的类层级出发,可对这四种角色分类。
- 相关簇
- 工厂接口和工厂接口实现类属于工厂相关簇,两者之间存在“高低层”的关系
- 抽象产品和产品实例都属于产品相关簇,两者之间存在“高低层”的关系
- 等级
- 工厂接口和抽象产品属于一个等级,属于底层等级,用于定义规范或框架。两者可以相互调用
- 工厂接口实现类和产品实例属于一个等级,属于高层等级,用于实现框架或规范。两者可以相互调用
四种角色对应关系的表格如下,列表示产品簇,行表示同等级
相关簇 | 相关簇 | |
---|---|---|
同等级 | 抽象产品 | 抽象工厂 |
同等级 | 实例产品 | 工厂实现类 |
四种角色对应UML图如下,列表示产品簇,行表示同等级
设计模式套路:抽象之间建立依赖关系,形成框架或规范;实例与抽象之间、实例与实例之间中进行实际的交互,实现OO的多态。
代码实践
步骤1 创建抽象产品。产品作为被操作对象,要先于同等级的工厂创建。
public abstract class AbstractProduct {
/**
* 产品抽象方法
*/
abstract void productAction();
/**
* 产品其他公有行为
*/
public void powerOn(){
System.out.println("打开电源");
}
/**
* 产品其他公有行为
*/
public void lightOn(){
System.out.println("打开灯光");
}
/**
* 产品其他公有行为
*/
public void dance(){
System.out.println("开始跳舞");
}
}
步骤2 创建工厂接口
public abstract class AbstractFactoryMethod {
/**
* 工厂方法,也是模式命名的来由
* @return
*/
abstract AbstractProduct createProduct();
/**
* 方法中用到了工厂方法
* 具体工厂方法生产什么产品根据当前类的子类决定
*/
public void productShow(){
/* 依赖倒置。从依赖具体product改为依赖抽象AbstractProduct
父类中用到了工厂方法,而工厂方法的实现是在子类中。*/
AbstractProduct product= createProduct();
product.powerOn();
product.lightOn();
product.dance();
}
}
注意,工厂接口中的工厂方法可以在任一地方使用,包括工厂接口内部。
步骤3 创建产品实例
/**
*小马
*/
public class Horse extends AbstractProduct {
@Override
void productAction() {
System.out.println("小马在奔跑");
}
}
/**
*小牛
*/
public class cow extends AbstractProduct {
@Override
void productAction() {
System.out.println("小牛在吃草");
}
}
步骤4 创建工厂实现类
/**
*小马工厂
*/
public class HorseFactory extends AbstractFactoryMethod {
@Override
AbstractProduct createProduct() {
return new Horse();
}
}
/**
*小牛工厂
*/
public class CowFacory extends AbstractFactoryMethod {
@Override
AbstractProduct createProduct() {
return new Cow();
}
}
以上代码的类图如下
![](https://i-blog.csdnimg.cn/blog_migrate/abb4075a958ff6c03feeca704202b2fa.png)
特点
-
优点
- 避免了简单工厂的三个缺点,即符合开闭原则、可以使用继承结构、避免因创建功能集中的工厂出错使系统瘫痪。
- 通过工厂名即可获取对应产品,不必关系产品的创建过程。
-
缺点
- 抽象方法只能生产一种产品
- 类爆炸问题
- 提高了系统的抽象性和理解难度
-
满足的设计原则
-
满足开闭原则
在简单工厂模式中,一旦需要生产新产品,都需要修改正在运行的历史代码,这不符合开闭原则。
在工厂方法模式中,添加新产品时,不需要修改正在运行的历史代码。只需要新产品实现抽象产品,同时添加一个对应的实现抽象工厂的产品工厂
-
满足依赖倒置原则
在不使用工厂方法模式的代码结构中,产品消费者依赖产品生产者,即高层依赖低层。
使用工厂方法模式后,产品消费者依赖抽象产品(业务关注产品提供的服务),具体的产品生产者也依赖抽象产品(抽象产品规定了产品该怎么生产,是规范),此时发生了依赖倒置(产品生产者(低层)依赖抽象产品(高层))
-
适用场景
同简单工厂模式类似,首先是需要创建产品的场景。特别地,当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式。
- 客户只知道产品的工厂名,不知道具体的产品名。譬如TCL电视工厂、小米手机工厂。
- 客户不关系产品创建过程,需要时再动态指定。可将具体工厂类的类名存储在配置文件或数据库中。
参考资料
- 工厂方法模式(详解版)
- Head First 设计模式(中文版)
- 工厂方法模式(Factory Method)-最易懂的设计模式解析
- 工厂方法