工厂方法模式:定义统一创建接口,让子类决定实例化哪类产品
大家好,我是冰点,今天我们接着聊设计模式,这次的主角是工厂方法模式。
前阵子帮朋友改代码时,发现他写了个“支付工具类”,里面用一堆if-else判断支付类型——要微信支付就new WeChatPay(),要支付宝支付就new Alipay(),还加了注释说“以后加银联支付再补个else if”。我当时就想,这要是后续再加十几种支付方式,这堆判断不得变成“面条代码”?而解决这种“创建逻辑混乱、扩展麻烦”的问题,正是工厂方法模式的拿手好戏。
为什么需要工厂方法模式?先解决“创建”的痛点
在聊具体实现前,我们得先搞懂:为什么不能直接new对象,非要用工厂来创建?我总结了两个最常见的场景痛点:
创建逻辑和业务逻辑混在一起。就像前面提到的支付案例,用户下单(业务逻辑)和创建支付对象(创建逻辑)写在一块,后续要改支付方式的创建规则,就得动业务代码,很容易引入bug。
扩展新产品时改动太大。如果现在要加“银联支付”,不仅要写
UnionPay类,还得去原来的工具类里加else if判断,这就违反了“开闭原则”——对扩展开放,对修改关闭。而工厂方法模式能让我们加新产品时,几乎不用改旧代码。
简单说,当你需要“统一管理对象创建,同时让新类型的扩展更灵活”时,工厂方法模式就该登场了。
工厂方法模式怎么实现?核心是“抽象工厂+具体工厂”
它的实现思路很清晰:把对象的创建权交给专门的“工厂类”,并且用抽象定义规范,让具体工厂对应具体产品。我还是用支付场景举例子,带你看完整实现逻辑。
第一步:定义统一的产品接口
先明确所有“支付产品”的共同行为,比如都要有“发起支付”的方法,这样后续不管加什么支付方式,都得遵守这个规范。
// 支付产品接口
public interface Pay {
// 发起支付的方法
void doPay(double amount);
}
第二步:实现具体的产品类
针对不同支付方式,写具体的实现类,比如微信支付和支付宝支付,各自实现doPay方法:
// 微信支付实现类
public class WeChatPay implements Pay {
@Override
public void doPay(double amount) {
System.out.println("使用微信支付:" + amount + "元");
}
}
// 支付宝支付实现类
public class Alipay implements Pay {
@Override
public void doPay(double amount) {
System.out.println("使用支付宝支付:" + amount + "元");
}
}
第三步:定义抽象工厂接口
接着定义工厂的“规范”——所有支付工厂,都得有“创建支付对象”的方法,这样后续加新工厂时,就不会乱了套。
// 支付工厂接口
public interface PayFactory {
// 创建支付对象的方法
Pay createPay();
}
第四步:实现具体的工厂类
给每个支付产品配一个专属工厂,比如微信支付工厂只创建WeChatPay对象,支付宝工厂只创建Alipay对象:
// 微信支付工厂
public class WeChatPayFactory implements PayFactory {
@Override
public Pay createPay() {
// 这里可以加微信支付的初始化逻辑,比如加载证书、配置参数
return new WeChatPay();
}
}
// 支付宝支付工厂
public class AlipayFactory implements PayFactory {
@Override
public Pay createPay() {
// 同理,可加支付宝的专属初始化逻辑
return new Alipay();
}
}
第五步:在业务中使用工厂
最后业务代码里,只需要根据需求选择对应的工厂,再调用工厂的createPay方法就能拿到支付对象,完全不用写new和if-else:
public class OrderService {
public void pay(String payType, double amount) {
// 这里可以用配置或枚举来获取工厂,避免硬编码
PayFactory factory;
if ("wechat".equals(payType)) {
factory = new WeChatPayFactory();
} else if ("alipay".equals(payType)) {
factory = new AlipayFactory();
} else {
throw new RuntimeException("不支持的支付方式");
}
// 通过工厂创建支付对象,调用支付方法
Pay pay = factory.createPay();
pay.doPay(amount);
}
}
实际用的时候,我总结了两个关键点
工厂方法模式不算复杂,但用的时候容易走偏,分享两个我踩过的坑和经验:
别把工厂搞成“万能创建器”。我见过有人把所有对象的创建都塞到一个工厂里,比如用同一个工厂既创建支付对象,又创建订单对象,这会让工厂变得臃肿。记住,一个工厂只负责一类产品的创建,比如“支付工厂”就只管支付相关,“订单工厂”就只管订单相关。
扩展新产品时的正确姿势。如果现在要加“银联支付”,只需要做两件事:一是写
UnionPay类实现Pay接口,二是写UnionPayFactory类实现PayFactory接口。原来的OrderService和旧工厂代码完全不用动,这才是符合“开闭原则”的扩展方式。
最后对比下:工厂方法和“直接new”的区别
可能有人会问,这么麻烦写工厂,直接new不也能实现吗?我用一张表总结下核心区别,你就懂了:
简单说,小项目、产品类型固定时,直接new没问题;但只要产品可能会扩展,或者创建逻辑复杂(比如需要初始化参数、加载配置),工厂方法模式带来的“灵活性”和“可维护性”就会特别明显。
接下来我们还会继续拆解其他设计模式,如果你对工厂方法模式有疑问,比如“怎么结合Spring的工厂来用”,或者想看看其他场景(比如商品创建)的实现,都可以告诉我。
评论区