工厂方法模式:定义、结构、实现、与简单工厂的区别、优缺点、应用场景
工厂方法模式,说白了就是「让子类决定要创建哪个对象」。
我刚开始接触设计模式时,觉得工厂方法跟简单工厂差不多。后来在项目中踩了坑,才真正理解它的价值。嗯,咱们今天就把这个模式彻底聊透。
一、定义
工厂方法模式(Factory Method Pattern)定义了一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化延迟到子类。
你想想看,这跟简单工厂最大的区别在哪?简单工厂是把所有创建逻辑塞到一个类里,而工厂方法是把创建逻辑分散到各个子类中。
核心思想:定义一个创建对象的抽象方法,由子类去实现具体的创建逻辑。
二、结构
工厂方法模式的结构其实很清晰,就四个角色:
- 抽象产品(Product):定义产品的公共接口
- 具体产品(ConcreteProduct):实现抽象产品接口的具体类
- 抽象工厂(Creator):声明工厂方法,返回一个产品对象
- 具体工厂(ConcreteCreator):实现工厂方法,创建具体的产品
我在项目中见过不少新手把抽象工厂和具体工厂混在一起写,结果代码耦合得一塌糊涂。记住,抽象工厂只负责「声明」,具体工厂才负责「实现」。
我的习惯:抽象工厂里除了工厂方法,通常还会包含一些公共的业务逻辑。这些逻辑不依赖具体产品,所以放在父类里很合适。
三、SVG 结构图
四、实现
咱们直接上代码。假设我们要做一个日志记录器,支持文件日志和数据库日志。
// 抽象产品
public interface Logger {
void log(String message);
}
// 具体产品A:文件日志
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("写入文件: " + message);
}
}
// 具体产品B:数据库日志
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("写入数据库: " + message);
}
}
// 抽象工厂
public abstract class LoggerFactory {
public abstract Logger createLogger();
// 公共业务逻辑
public void writeLog(String message) {
Logger logger = createLogger();
logger.log(message);
}
}
// 具体工厂A
public class FileLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger();
}
}
// 具体工厂B
public class DatabaseLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
return new DatabaseLogger();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
LoggerFactory factory = new FileLoggerFactory();
factory.writeLog("用户登录成功");
}
}
我曾经踩过的坑:在工厂方法里做太多初始化工作。比如在 createLogger() 里读取配置文件、建立数据库连接。这会让工厂方法变得臃肿,也违背了单一职责原则。工厂方法只负责「创建」,初始化逻辑应该放在别处。
五、与简单工厂的区别
这个问题面试经常问。我直接给你列个表,一目了然:
| 对比维度 | 简单工厂 | 工厂方法 |
|---|---|---|
| 创建逻辑位置 | 集中在一个工厂类 | 分散在各个子工厂 |
| 开闭原则支持 | 不满足(新增产品需修改工厂) | 满足(新增产品只需新增子工厂) |
| 复杂度 | 低 | 中等 |
| 适用场景 | 产品种类少且稳定 | 产品种类多且经常扩展 |
说白了,简单工厂适合「小打小闹」,工厂方法适合「正经项目」。我个人的习惯是:如果产品不超过3个,用简单工厂;超过3个或者不确定未来会不会增加,直接用工厂方法。
六、优缺点
优点
- 符合开闭原则:新增产品时,不需要修改现有代码,只需新增一个具体工厂类
- 符合单一职责原则:每个工厂只负责创建一种产品
- 解耦:客户端只依赖抽象工厂和抽象产品,不依赖具体实现
- 扩展性好:产品线扩展时,代码改动量小
缺点
- 类数量增加:每新增一个产品,就要新增一个具体工厂类
- 复杂度提升:相比简单工厂,结构更复杂
- 抽象层增加:客户端需要理解抽象工厂和抽象产品的概念
我的建议:如果产品种类不多(比如3-5个),而且不太可能扩展,用简单工厂就够了。别为了用设计模式而用设计模式,过度设计比没有设计更可怕。
七、应用场景
工厂方法模式在实际项目中很常见。我列举几个典型的场景:
- 日志框架:支持文件日志、数据库日志、控制台日志等
- 数据库访问:支持 MySQL、PostgreSQL、Oracle 等不同数据库
- 文档转换器:支持 PDF、Word、Excel 等格式转换
- UI 组件库:根据平台创建不同风格的按钮、文本框等
- 消息队列:支持 RabbitMQ、Kafka、RocketMQ 等
我记得有一次做支付系统集成,需要对接微信支付、支付宝、银联。每个支付渠道的接口、签名方式、回调处理都不一样。用工厂方法模式,每个渠道一个工厂,客户端只需要知道「我要支付」,具体用哪个渠道由配置决定。后来新增了京东支付,只加了一个工厂类,现有代码一行没改。嗯,这就是工厂方法的魅力。
总结一下:工厂方法模式的核心是「把创建逻辑延迟到子类」。它解决了简单工厂的扩展性问题,但代价是增加了类的数量。选择哪种模式,取决于你的项目规模和未来的变化预期。