工厂模式
工厂模式用于封装对象创建逻辑,让客户端不直接依赖具体类。
当创建对象的逻辑开始变复杂,或者对象类型会随着业务变化不断增加时,就可以考虑工厂模式。
解决什么问题
直接在业务代码里到处 new 会带来几个问题:
- 客户端知道太多具体类。
- 创建逻辑散落在各个地方。
- 新增类型时要修改很多业务代码。
- 构造参数复杂时,调用方很容易写错。
工厂模式把“创建哪个对象、怎么创建”集中到专门的工厂中。
简单工厂
简单工厂不是 GoF 经典 23 种之一,但实际开发中非常常见。
场景
订单系统支持多种支付方式:微信、支付宝、银行卡。
java
interface PayClient {
void pay(long orderId, int amount);
}
class WechatPayClient implements PayClient {
public void pay(long orderId, int amount) {
System.out.println("wechat pay");
}
}
class AlipayClient implements PayClient {
public void pay(long orderId, int amount) {
System.out.println("alipay pay");
}
}工厂负责创建:
java
class PayClientFactory {
public static PayClient create(String type) {
if ("wechat".equals(type)) {
return new WechatPayClient();
}
if ("alipay".equals(type)) {
return new AlipayClient();
}
throw new IllegalArgumentException("unsupported pay type: " + type);
}
}使用:
java
PayClient client = PayClientFactory.create("wechat");
client.pay(1001L, 5000);特点
简单工厂能把对象创建集中起来,但新增类型时仍然要修改工厂类,所以不完全符合开闭原则。
适合类型较少、变化不频繁的场景。
工厂方法
工厂方法把创建逻辑延迟到子类,每个具体工厂负责创建一种产品。
java
interface PayClientFactory {
PayClient create();
}
class WechatPayClientFactory implements PayClientFactory {
public PayClient create() {
return new WechatPayClient();
}
}
class AlipayClientFactory implements PayClientFactory {
public PayClient create() {
return new AlipayClient();
}
}使用:
java
PayClientFactory factory = new WechatPayClientFactory();
PayClient client = factory.create();工厂方法更符合开闭原则。新增支付方式时,新建一个产品类和一个工厂类即可,不需要修改原来的工厂。
缺点是类数量会增加。
抽象工厂
抽象工厂用于创建一组相关对象,也叫“产品族”。
场景
不同云厂商都提供短信和对象存储能力:
text
腾讯云产品族:TencentSmsClient + TencentOssClient
阿里云产品族:AliyunSmsClient + AliyunOssClient抽象产品:
java
interface SmsClient {
void send(String phone, String message);
}
interface OssClient {
void upload(String fileName);
}抽象工厂:
java
interface CloudFactory {
SmsClient createSmsClient();
OssClient createOssClient();
}具体工厂:
java
class TencentCloudFactory implements CloudFactory {
public SmsClient createSmsClient() {
return new TencentSmsClient();
}
public OssClient createOssClient() {
return new TencentOssClient();
}
}
class AliyunCloudFactory implements CloudFactory {
public SmsClient createSmsClient() {
return new AliyunSmsClient();
}
public OssClient createOssClient() {
return new AliyunOssClient();
}
}当系统需要整体切换云厂商时,只要切换工厂即可。
三者区别
| 类型 | 关注点 | 适合场景 | 主要问题 |
|---|---|---|---|
| 简单工厂 | 一个工厂创建多种产品 | 类型少,变化少 | 新增产品要改工厂 |
| 工厂方法 | 一个工厂创建一种产品 | 产品经常扩展 | 类数量增加 |
| 抽象工厂 | 创建一组相关产品 | 产品族整体切换 | 新增产品等级困难 |
使用场景
- 根据配置创建不同实现。
- 第三方渠道接入,例如支付、短信、存储、物流。
- 对象创建过程复杂,不希望业务代码关心细节。
- 框架需要根据类型创建处理器。
实战注意
- 类型少时,简单工厂就够了。
- 产品不断增加时,再考虑工厂方法。
- 需要成套切换一组实现时,再考虑抽象工厂。
- 在 Spring 中,经常可以用
Map<String, Interface>替代大量手写工厂。