目 录CONTENT

文章目录

工厂方法模式:定义统一创建接口,让子类决定实例化

冰点
2025-12-04 / 0 评论 / 0 点赞 / 6 阅读 / 18563 字

工厂方法模式:定义统一创建接口,让子类决定实例化哪类产品

大家好,我是冰点,今天我们接着聊设计模式,这次的主角是工厂方法模式。

前阵子帮朋友改代码时,发现他写了个“支付工具类”,里面用一堆if-else判断支付类型——要微信支付就new WeChatPay(),要支付宝支付就new Alipay(),还加了注释说“以后加银联支付再补个else if”。我当时就想,这要是后续再加十几种支付方式,这堆判断不得变成“面条代码”?而解决这种“创建逻辑混乱、扩展麻烦”的问题,正是工厂方法模式的拿手好戏。

为什么需要工厂方法模式?先解决“创建”的痛点

在聊具体实现前,我们得先搞懂:为什么不能直接new对象,非要用工厂来创建?我总结了两个最常见的场景痛点:

  1. 创建逻辑和业务逻辑混在一起。就像前面提到的支付案例,用户下单(业务逻辑)和创建支付对象(创建逻辑)写在一块,后续要改支付方式的创建规则,就得动业务代码,很容易引入bug。

  2. 扩展新产品时改动太大。如果现在要加“银联支付”,不仅要写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);
    }
}

实际用的时候,我总结了两个关键点

工厂方法模式不算复杂,但用的时候容易走偏,分享两个我踩过的坑和经验:

  1. 别把工厂搞成“万能创建器”。我见过有人把所有对象的创建都塞到一个工厂里,比如用同一个工厂既创建支付对象,又创建订单对象,这会让工厂变得臃肿。记住,一个工厂只负责一类产品的创建,比如“支付工厂”就只管支付相关,“订单工厂”就只管订单相关。

  2. 扩展新产品时的正确姿势。如果现在要加“银联支付”,只需要做两件事:一是写UnionPay类实现Pay接口,二是写UnionPayFactory类实现PayFactory接口。原来的OrderService和旧工厂代码完全不用动,这才是符合“开闭原则”的扩展方式。

最后对比下:工厂方法和“直接new”的区别

可能有人会问,这么麻烦写工厂,直接new不也能实现吗?我用一张表总结下核心区别,你就懂了:

对比维度

直接new对象

工厂方法模式

创建逻辑位置

和业务逻辑混在一起

单独放在工厂类,职责分离

扩展新产品

需修改原有业务代码(加if)

只需新增产品类和工厂类,不修改旧代码

代码复杂度

简单场景下低,复杂场景高

初期略高,后期维护成本低

简单说,小项目、产品类型固定时,直接new没问题;但只要产品可能会扩展,或者创建逻辑复杂(比如需要初始化参数、加载配置),工厂方法模式带来的“灵活性”和“可维护性”就会特别明显。

接下来我们还会继续拆解其他设计模式,如果你对工厂方法模式有疑问,比如“怎么结合Spring的工厂来用”,或者想看看其他场景(比如商品创建)的实现,都可以告诉我。

0

评论区