工厂模式
模式概述
定义
工厂模式是一种创建型设计模式,它提供了一种将对象的实例化过程封装在工厂类中的方式。根据实现方式的不同,工厂模式可以分为简单工厂、工厂方法和抽象工厂三种类型。
在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
简单工厂模式是工厂方法模式的一种特例,不属于 GOF 的 23 中经典设计模式
通俗理解
想象你去一家披萨店点披萨。你不需要自己去厨房制作披萨,只需要告诉服务员你想要什么口味的披萨(比如海鲜披萨或者水果披萨),服务员会把你的需求转达给厨房(工厂),厨房就会制作相应的披萨。这就是工厂模式的基本思想 - 你只需要告诉工厂你要什么,工厂负责创建对应的产品给你。
为什么要用工厂模式?
假设你正在开发一个应用程序,需要处理不同类型的文件(JSON、XML、YAML等)。如果直接在代码中使用new来创建对象:
// 不好的做法
if (fileType.equals("json")) {
parser = new JsonParser();
} else if (fileType.equals("xml")) {
parser = new XmlParser();
} else if (fileType.equals("yaml")) {
parser = new YamlParser();
}
这样的代码有以下问题:
- 创建代码散布在各处,难以维护
- 每次添加新的文件类型都要修改代码
- 代码重复,违反了DRY原则
使用工厂模式后:
// 好的做法
Parser parser = ParserFactory.createParser(fileType);
生活中的例子
-
餐厅点餐
- 顾客(客户端)不需要知道厨房(工厂)如何制作食物
- 服务员(工厂接口)接收订单
- 厨师(具体工厂)负责制作具体的菜品
-
汽车制造
- 4S店(客户端)向工厂订购汽车
- 汽车制造厂(工厂)负责生产具体型号的汽车
- 不同型号的汽车由不同的生产线(具体工厂)生产
工厂模式分类与实现
工厂模式的演进
1. 简单工厂(Simple Factory)
简单工厂模式不是一种设计模式,反而更像是一种编程习惯(面向抽象(接口)编程 )。简单工厂模式又叫做静态工厂方法模式(static Factory Method pattern),它是通过静态方法接受不同的参数来返回不同的实例对象。
实现方式:定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
适用场景:
- 需要创建的对象较少
- 客户端不关心对象的创建过程。
简单工厂包含如下角色:
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品:实现或者继承抽象产品的子类。
- 提供了创建产品的方法,调用者通过该方法来获取产品。


// 简单工厂实现
public class RuleConfigParserFactory {
// 这个方法就像一个万能工人,根据需求制作不同的产品
public static IRuleConfigParser createParser(String configFormat) {
// 创建解析器对象
IRuleConfigParser parser = null;
// 根据配置文件格式选择对应的解析器
if ("json".equalsIgnoreCase(configFormat)) {
parser = new JsonRuleConfigParser(); // 创建JSON解析器
} else if ("xml".equalsIgnoreCase(configFormat)) {
parser = new XmlRuleConfigParser(); // 创建XML解析器
}
return parser;
}
}
优点:
- 简单直观,容易理解
- 适合产品种类较少的情况
- 封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建和业务逻辑分开,避免了可能会修改客户代码的问题。
- 如果要修改新产品,直接修改工厂类,不需要在源代码中进行修改,降低了客户端修改代码的可能性,更加容易扩展。
缺点:
- 如果要添加新的产品,还是需要修改工厂类的代码,违背了开闭原则。
- 工厂类职责过重,违反单一职责原则
优化版本:使用工厂类缓存
public class RuleConfigParserFactory {
private static final Map<String, IRuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
cachedParsers.put("properties", new PropertiesRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
return cachedParsers.get(configFormat.toLowerCase());
}
}
2. 工厂方法(Factory Method)
简单工厂模式存在一个明显的缺点,就是在增加新产品的时候,还是需要修改工厂类的代码的,这就违背了开闭原则。
定义一个用于创建对象的接口,让子类决定实例化那个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式的目的很简单,就是 封装对象创建的过程,提升创建对象方法的可复用性。 工厂方法模式的主要角色:
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法。
- 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品: 定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品:实现了抽象产品角色所定义的接口,由具体的工厂来创建,它同具体工厂之间一一对应。


当对象的创建逻辑比较复杂,不只是简单的new一下就可以,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式。 这种模式更像是一个工厂集团,每个工厂专门负责生产一种产品。
// 工厂接口 - 定义了所有工厂都必须实现的方法
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser(); // 创建解析器的方法
}
// JSON解析器工厂 - 专门负责创建JSON解析器
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser(); // 创建JSON解析器
}
}
public class XmlConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
为什么这样设计更好?
- 每个工厂类只负责创建一种产品,职责单一
- 添加新产品只需要添加新的工厂类,不需要修改现有代码
- 符合开闭原则(对扩展开放,对修改关闭)
// 工厂的工厂
public class ConfigParserFactoryMap {
private static final Map<String, IConfigParserFactory> cachedFactories = new HashMap<>();
static {
cachedFactories.put("json", new JsonConfigParserFactory());
cachedFactories.put("xml", new XmlConfigParserFactory());
}
public static IConfigParserFactory getParserFactory(String type) {
if (type == null || type.isEmpty()) {
return null;
}
return cachedFactories.get(type.toLowerCase());
}
}
优点:
- 用户只需要知道具体工厂的名字,就可以获取到想要的产品,不需要关注产品的创建过程。
- 在系统新增加产品的时候,只需要添加具体产品类和对应的具体工厂,不需要对原工厂进行修改,满足了开闭原则。
缺点:
- 每增加一个产品,就需要一个具体的产品类和对应的工厂类,这样会增加系统的复杂度。
3. 抽象工厂(Abstract Factory)
抽象工厂模式比工厂方法模式的抽象程度更高,在工厂方法模式中每一个具体工厂只需要生产一种具体产品,但是在抽象工厂模式中一个具体工厂可以生产一组相关的具体产品,这样一组产品被称为产品族,产品族中的每一个产品成分都属于某一个继承等级结构。
产品等级结构与产品组
- 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL 电视机,则抽象电视机与具体电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电 视机是其子类。
- 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱、海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂为创建一组对象提供了解决方案,与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,而是负责创建一个产品族,这种模式就像是一个超级工厂,可以生产多个产品系列。比如,一个工厂不仅可以生产轮胎,还可以生产发动机、方向盘等。


// 抽象产品等级接口
public interface IRuleConfigParser {
void parseRule(String ruleConfig);
}
public interface ISystemConfigParser {
void parseSystem(String systemConfig);
}
// 具体产品实现
public class JsonRuleConfigParser implements IRuleConfigParser {
@Override
public void parseRule(String ruleConfig) {
System.out.println("解析JSON规则配置: " + ruleConfig);
}
}
public class JsonSystemConfigParser implements ISystemConfigParser {
@Override
public void parseSystem(String systemConfig) {
System.out.println("解析JSON系统配置: " + systemConfig);
}
}
public class XmlRuleConfigParser implements IRuleConfigParser {
@Override
public void parseRule(String ruleConfig) {
System.out.println("解析XML规则配置: " + ruleConfig);
}
}
public class XmlSystemConfigParser implements ISystemConfigParser {
@Override
public void parseSystem(String systemConfig) {
System.out.println("解析XML系统配置: " + systemConfig);
}
}
// 抽象工厂接口
public interface IConfigParserFactory {
IRuleConfigParser createRuleParser();
ISystemConfigParser createSystemParser();
}
// 具体工厂实现
public class JsonConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleParser() {
return new JsonRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemParser() {
return new JsonSystemConfigParser();
}
}
public class XmlConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleParser() {
return new XmlRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemParser() {
return new XmlSystemConfigParser();
}
}
从上面的代码实现中可以看出,抽象工厂模式向使用(客户)方隐藏了下列变化:
- 程序所支持的实例集合(具体工厂)的数目。
- 当前使用的是集合中的哪一个实例 ;
- 在任意时刻被实例化的具体类型;
在理解抽象工厂原理时,要 考虑 如何找到某一类产品的争取共性功能 。
JDBC 的实现就是抽象工厂模式,无论使用什么样的数据库,只要数据库支持 JDBC ,就能对数据库进行读写操作。 还有的示例是日志收集工厂。
优点
- 对于不同产品系列有比较多共性特征时,可以使用抽象工厂模式,有助于提升组件的复用性。
- 当需要提升代码的扩展性并降低维护成本时,把对象的创建和使用过程分开,能有效地将代码统一到一个级别上。
- 解决跨平台带来的兼容性问题。
缺点
- 增加新的产品等级结构麻烦,需要对原有结构进行较大的修改,甚至需要修改抽象层代码,这显然会代码较大的不变,违背了开闭原则。
工厂模式的应用场景
-
封装变化:
- 创建对象的过程可能会随时发生变化
- 需要屏蔽对象创建的细节
-
代码复用:
- 创建对象的代码很复杂
- 需要在多个地方创建同类对象
-
隔离复杂性:
- 将对象的创建和使用分离
- 降低代码的耦合度
-
控制复杂度:
- 当系统中的对象种类较多时
- 通过工厂模式进行分类管理
如何选择工厂模式?
-
使用简单工厂:
- 工厂类负责创建的对象比较少
- 客户端只需要传入工厂类的参数,对于如何创建对象不关心
-
使用工厂方法:
- 客户端需要决定实例化哪一个工厂来实现运行时的动态变化
- 多个工厂类对应多个产品类,每个工厂创建一种产品
-
使用抽象工厂:
- 需要创建的产品族具有相同的约束
- 产品等级结构稳定,设计完成之后不会向系统中增加新的产品等级结构