老板让我一天加 3 种支付方式,我用工厂模式躺赢了

工厂模式:从业务痛点到代码落地的创建型方案😎

在日常开发中,我们总会遇到对象创建的场景。比如做一个电商系统,需要创建商品、订单、用户等对象;做一个绘图工具,需要创建圆形、矩形、三角形等图形对象。刚开始可能觉得直接用new关键字创建很简单,但随着业务迭代,问题会慢慢暴露 ------ 当对象创建逻辑变复杂,或者需要频繁新增同类对象时,代码会变得臃肿难维护。这时候,工厂模式就能派上大用场。

一、为什么需要工厂模式?先看个真实痛点

假设我们要开发一个简单的支付系统,初期只支持支付宝支付。代码可能是这样的(以 Java 为例):

typescript 复制代码
// 支付宝支付类
public class Alipay {
    public void pay(double amount) {
        System.out.println("使用支付宝支付" + amount + "元");
    }
}
// 业务调用处
public class PaymentService {
    public void processPayment(String type, double amount) {
        // 直接new对象
        Alipay alipay = new Alipay();
        alipay.pay(amount);
    }
}

后来业务扩展,需要新增微信支付。这时候就得修改PaymentService里的代码,新增WechatPay类,再在processPayment里加判断:

typescript 复制代码
// 新增微信支付类
public class WechatPay {
    public void pay(double amount) {
        System.out.println("使用微信支付" + amount + "元");
    }
}
// 修改业务调用处
public class PaymentService {
    public void processPayment(String type, double amount) {
        if ("alipay".equals(type)) {
            Alipay alipay = new Alipay();
            alipay.pay(amount);
        } else if ("wechat".equals(type)) { // 新增判断
            WechatPay wechatPay = new WechatPay();
            wechatPay.pay(amount);
        }
    }
}

再后来要加银联支付、ApplePay 呢?processPayment里的if-else会越来越长,每次新增支付方式都要修改原有代码 ------ 这既违反了 "开闭原则"(对扩展开放、对修改关闭),也让代码维护成本越来越高。

而工厂模式的核心作用,就是把 "对象创建" 和 "对象使用" 拆分开,让创建逻辑集中在专门的 "工厂" 里,业务代码只需要调用工厂,不用关心对象是怎么造出来的。

二、三种工厂模式:从简单到复杂的演进

工厂模式不是单一的一种模式,而是分了三个层级,对应不同的业务复杂度。我们从简单的开始讲,逐步深入。

1. 简单工厂模式:适用于产品类型较少的场景

简单工厂模式也叫 "静态工厂模式",核心是用一个工厂类,根据传入的参数来创建不同的产品对象。就像一个 "小卖部",告诉老板要什么,老板直接给你对应的商品。

代码落地:重构支付系统

首先定义一个支付接口,统一所有支付方式的行为(这是关键,让不同支付类有共同的标准):

csharp 复制代码
// 支付接口
public interface Payment {
    // 统一支付方法
    void pay(double amount);
}

然后实现具体的支付类(支付宝、微信),都实现Payment接口:

java 复制代码
// 支付宝支付实现
public class Alipay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount + "元");
    }
}
// 微信支付实现
public class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付:" + amount + "元");
    }
}

接下来创建 "工厂类",把对象创建逻辑集中在这里:

typescript 复制代码
// 支付工厂(简单工厂)
public class PaymentFactory {
    // 静态方法,根据类型创建支付对象
    public static Payment createPayment(String type) {
        if (type == null) {
            return null;
        }
        // 根据传入的类型,返回对应的支付对象
        switch (type.toLowerCase()) {
            case "alipay":
                return new Alipay();
            case "wechat":
                return new WechatPay();
            default:
                throw new IllegalArgumentException("不支持的支付方式:" + type);
        }
    }
}

最后看业务调用处的变化 ------ 不用再写if-else,直接找工厂要对象:

typescript 复制代码
// 业务服务类
public class PaymentService {
    public void processPayment(String type, double amount) {
        // 找工厂创建对象,不用关心创建细节
        Payment payment = PaymentFactory.createPayment(type);
        payment.pay(amount);
    }
}
// 测试代码
public class Test {
    public static void main(String[] args) {
        PaymentService service = new PaymentService();
        service.processPayment("alipay", 199.9); // 输出:支付宝支付:199.9元
        service.processPayment("wechat", 299.5);  // 输出:微信支付:299.5元
    }
}
优缺点很明显
  • 优点:业务代码变简洁了,新增支付方式时,只需要新增支付类和修改工厂的switch,不用改业务逻辑。
  • 缺点:工厂类会随着产品增多而变大(比如加银联、ApplePay,就要在工厂里加case),还是会违反 "开闭原则"。所以简单工厂适合产品类型少、变动不频繁的场景。

2. 工厂方法模式:解决简单工厂的 "扩展痛点"

如果支付方式会频繁新增(比如每月都要加 1-2 种),简单工厂的switch会越来越长。这时候就需要 "工厂方法模式"------ 它把原来的 "一个工厂" 拆成 "多个工厂",每个产品对应一个专属工厂,新增产品时只需要新增产品类和对应的工厂类,不用改原有代码。

就像把 "小卖部" 改成 "连锁店":买支付宝支付找 "支付宝工厂",买微信支付找 "微信工厂",新增银联支付时,直接开个 "银联工厂" 就行。

代码落地:再重构支付系统

首先,保留之前的Payment接口和具体支付类(Alipay、WechatPay),然后新增 "工厂接口"------ 定义所有工厂的统一行为:

csharp 复制代码
// 支付工厂接口(所有具体工厂都要实现这个接口)
public interface PaymentFactory {
    // 工厂的核心方法:创建支付对象
    Payment createPayment();
}

然后为每个支付方式创建专属工厂:

typescript 复制代码
// 支付宝工厂:只创建支付宝对象
public class AlipayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new Alipay();
    }
}
// 微信工厂:只创建微信支付对象
public class WechatPayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new WechatPay();
    }
}

业务调用处也要调整 ------ 现在需要先创建对应的工厂,再用工厂创建支付对象:

java 复制代码
// 业务服务类
public class PaymentService {
    public void processPayment(PaymentFactory factory, double amount) {
        // 用工厂创建对象
        Payment payment = factory.createPayment();
        payment.pay(amount);
    }
}
// 测试代码
public class Test {
    public static void main(String[] args) {
        PaymentService service = new PaymentService();
        // 支付宝支付:传支付宝工厂
        service.processPayment(new AlipayFactory(), 199.9); 
        // 微信支付:传微信工厂
        service.processPayment(new WechatPayFactory(), 299.5); 
    }
}
新增产品有多简单?

现在要加 "银联支付",只需要做两步:

  1. 新增银联支付类:
java 复制代码
public class UnionPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("银联支付:" + amount + "元");
    }
}
  1. 新增银联工厂类:
typescript 复制代码
public class UnionPayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new UnionPay();
    }
}

业务调用时直接传UnionPayFactory就行,不用改任何原有代码 ------ 完全符合 "开闭原则"。

适用场景

产品类型多、且频繁新增的场景,比如电商的商品类型(实物、虚拟、服务)、物流系统的配送方式(快递、自提、同城送)。

3. 抽象工厂模式:处理 "产品族" 场景

有时候我们遇到的不是 "单一产品",而是 "产品族"------ 比如做一个跨平台的 UI 组件库,需要为 Windows 和 Mac 系统分别提供 "按钮" 和 "文本框":Windows 的按钮 + Windows 的文本框是一套,Mac 的按钮 + Mac 的文本框是另一套。这时候工厂方法模式就不够用了,因为它只能创建单一产品,而抽象工厂模式可以创建 "一整套相关产品"。

代码落地:跨平台 UI 组件库

首先定义两个产品接口(按钮、文本框),代表产品族里的不同产品:

csharp 复制代码
// 按钮接口
public interface Button {
    void render(); // 渲染按钮
}
// 文本框接口
public interface TextBox {
    void render(); // 渲染文本框
}

然后实现不同平台的具体产品:

typescript 复制代码
// Windows按钮
public class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Windows风格按钮");
    }
}
// Windows文本框
public class WindowsTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("渲染Windows风格文本框");
    }
}
// Mac按钮
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Mac风格按钮");
    }
}
// Mac文本框
public class MacTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("渲染Mac风格文本框");
    }
}

接下来定义 "抽象工厂接口"------ 注意:它的方法不是创建单一产品,而是创建产品族里的所有产品:

csharp 复制代码
// UI组件工厂接口(抽象工厂)
public interface UIFactory {
    Button createButton(); // 创建按钮
    TextBox createTextBox(); // 创建文本框
}

然后实现不同平台的具体工厂,每个工厂负责创建对应平台的一整套组件:

typescript 复制代码
// Windows UI工厂:创建Windows全套组件
public class WindowsUIFactory implements UIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
    @Override
    public TextBox createTextBox() {
        return new WindowsTextBox();
    }
}
// Mac UI工厂:创建Mac全套组件
public class MacUIFactory implements UIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }
    @Override
    public TextBox createTextBox() {
        return new MacTextBox();
    }
}

最后看业务调用 ------ 只需要确定用哪个平台的工厂,就能拿到一整套组件:

java 复制代码
// UI渲染服务
public class UIRenderer {
    private Button button;
    private TextBox textBox;
    // 传入抽象工厂,初始化一整套组件
    public UIRenderer(UIFactory factory) {
        this.button = factory.createButton();
        this.textBox = factory.createTextBox();
    }
    // 渲染所有组件
    public void renderUI() {
        button.render();
        textBox.render();
    }
}
// 测试代码
public class Test {
    public static void main(String[] args) {
        // Windows平台:用Windows工厂
        UIRenderer windowsRenderer = new UIRenderer(new WindowsUIFactory());
        windowsRenderer.renderUI(); 
        // 输出:渲染Windows风格按钮 + 渲染Windows风格文本框
        // Mac平台:用Mac工厂
        UIRenderer macRenderer = new UIRenderer(new MacUIFactory());
        macRenderer.renderUI();
        // 输出:渲染Mac风格按钮 + 渲染Mac风格文本框
    }
}
核心价值

抽象工厂模式的关键是 "产品族" 的概念 ------ 它保证了同一工厂创建的产品是 "配套" 的(比如 Windows 工厂不会造出 Mac 按钮),避免出现 "Windows 按钮 + Mac 文本框" 这种不兼容的组合。

适用场景

需要创建 "一整套相关产品",且产品之间有兼容性要求的场景,比如:跨平台软件(Windows/Mac/Linux)、数据库访问(MySQL/Oracle/PostgreSQL 的连接 + 查询组件)、不同品牌的硬件驱动(华为 / 小米的手机 + 平板驱动)。

三、总结:三种工厂模式怎么选?

很多人学完工厂模式会混淆,其实核心是看业务需求里的 "产品复杂度":

模式类型 核心特点 适用场景 代码复杂度
简单工厂模式 一个工厂造所有产品 产品少、变动少(如简单支付)
工厂方法模式 一个产品对应一个工厂 产品多、频繁新增(如多支付方式)
抽象工厂模式 一个工厂造一整套产品族 需创建配套产品(如跨平台 UI)

本质上,三种模式是 "逐步解耦" 的过程:从 "硬编码 new 对象" 到 "集中工厂创建",再到 "按产品 / 产品族拆分工厂",最终目的都是让代码更易维护、易扩展。

在实际开发中,不用一开始就追求最复杂的抽象工厂 ------ 比如做一个小工具,用简单工厂就够了;等业务涨到需要频繁加产品时,再重构为工厂方法模式也不迟。设计模式的核心是 "解决问题",而不是 "炫技"。

相关推荐
简单点了42 分钟前
SM4加密算法
java·开发语言
用户3721574261351 小时前
Java 实现HTML转Word:从HTML文件与字符串到可编辑Word文档
java
yvya_1 小时前
Mybatis总结
java·spring·mybatis
姜太小白1 小时前
【VSCode】VSCode为Java C/S项目添加图形用户界面
java·c语言·vscode
一路向北North2 小时前
java将doc文件转pdf
java·pdf
咕白m6252 小时前
Java 开发:用 Spire.PDF 高效压缩 PDF 文件
java·编程语言
万行2 小时前
点评项目(Redis中间件)&第一部分Redis基础
java·数据库·redis·缓存·中间件
用户2707912938182 小时前
异常处理!Exception 和 Error 有什么区别?
java
藤椒鱼不爱编程3 小时前
核心类库_常用类
java·编程语言
杨杨杨大侠3 小时前
第5篇:日志处理器的核心逻辑 - 让日志更智能
java·spring·apache log4j