老板让我一天加 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 对象" 到 "集中工厂创建",再到 "按产品 / 产品族拆分工厂",最终目的都是让代码更易维护、易扩展。

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

相关推荐
达文汐2 分钟前
【困难】力扣算法题解析LeetCode332:重新安排行程
java·数据结构·经验分享·算法·leetcode·力扣
培风图南以星河揽胜2 分钟前
Java版LeetCode热题100之零钱兑换:动态规划经典问题深度解析
java·leetcode·动态规划
启山智软26 分钟前
【中大企业选择源码部署商城系统】
java·spring·商城开发
我真的是大笨蛋28 分钟前
深度解析InnoDB如何保障Buffer与磁盘数据一致性
java·数据库·sql·mysql·性能优化
怪兽源码1 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
恒悦sunsite1 小时前
Redis之配置只读账号
java·redis·bootstrap
梦里小白龙1 小时前
java 通过Minio上传文件
java·开发语言
人道领域1 小时前
javaWeb从入门到进阶(SpringBoot事务管理及AOP)
java·数据库·mysql
sheji52612 小时前
JSP基于信息安全的读书网站79f9s--程序+源码+数据库+调试部署+开发环境
java·开发语言·数据库·算法
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于Java Web的电子商务网站的用户行为分析与个性化推荐系统为例,包含答辩的问题和答案
java·开发语言