9、工厂模式实战:支付网关抽象工厂、解析不同格式文件(JSON/XML/CSV)的工厂方法

工厂模式这东西,说白了就是帮你把「创建对象」这件事给封装起来。很多新手写代码,new 来 new 去,到处散落着对象创建的逻辑。等业务一复杂,改一处要牵连十处,那叫一个痛苦。

我个人习惯是:只要发现某个类会被频繁创建,或者创建逻辑可能变化,我就立刻上工厂。今天咱们就拿两个真实场景来拆解——支付网关和文件解析。这两个场景,我在项目中都踩过坑,今天一并分享给你。

9.1 支付网关抽象工厂:多商户、多渠道的噩梦

先说说支付。你想想看,一个电商平台,可能要对接微信支付、支付宝、银联、甚至 PayPal。每个支付渠道的接口、签名方式、回调处理都不一样。如果你在业务代码里写满 if-else,那后期维护就是一场灾难。

抽象工厂模式正好解决这个问题。它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。

9.1.1 支付网关的核心抽象

我们先定义支付网关的抽象。每个支付网关,至少需要三个核心组件:支付请求、支付响应、回调处理。

// 支付请求抽象
public interface PaymentRequest {
    String buildSign();
    Map<String, String> toMap();
}

// 支付响应抽象
public interface PaymentResponse {
    boolean isSuccess();
    String getTransactionId();
    String getRawResponse();
}

// 回调处理抽象
public interface PaymentCallback {
    boolean verifySign(Map<String, String> params);
    String handleCallback(Map<String, String> params);
}

有了抽象,我们再定义抽象工厂:

public interface PaymentGatewayFactory {
    PaymentRequest createPaymentRequest(double amount, String orderId);
    PaymentResponse createPaymentResponse(String rawResponse);
    PaymentCallback createPaymentCallback();
}

9.1.2 具体工厂实现:微信支付 vs 支付宝

接下来,每个支付渠道实现自己的工厂。以微信支付为例:

public class WechatPaymentFactory implements PaymentGatewayFactory {

    @Override
    public PaymentRequest createPaymentRequest(double amount, String orderId) {
        return new WechatPaymentRequest(amount, orderId);
    }

    @Override
    public PaymentResponse createPaymentResponse(String rawResponse) {
        return new WechatPaymentResponse(rawResponse);
    }

    @Override
    public PaymentCallback createPaymentCallback() {
        return new WechatPaymentCallback();
    }
}

// 支付宝类似
public class AlipayPaymentFactory implements PaymentGatewayFactory {
    // 实现类似,略
}
核心要点: 客户端代码只依赖 PaymentGatewayFactory 接口,不依赖任何具体实现。切换支付渠道时,只需更换工厂实例。

9.1.3 客户端如何使用

public class PaymentService {
    private PaymentGatewayFactory factory;

    // 通过依赖注入或配置决定工厂
    public PaymentService(PaymentGatewayFactory factory) {
        this.factory = factory;
    }

    public String pay(double amount, String orderId) {
        PaymentRequest request = factory.createPaymentRequest(amount, orderId);
        // 发送请求...
        String rawResponse = sendHttpRequest(request.toMap());
        PaymentResponse response = factory.createPaymentResponse(rawResponse);
        return response.getTransactionId();
    }

    public String handleCallback(Map<String, String> params) {
        PaymentCallback callback = factory.createPaymentCallback();
        if (callback.verifySign(params)) {
            return callback.handleCallback(params);
        }
        return "FAIL";
    }
}

我曾经在一个项目中,一开始只接了微信支付,代码里直接 new WechatPaymentRequest。后来要加支付宝,改得我头皮发麻。重构为抽象工厂后,新增一个渠道只需要加一个工厂类和几个实现类,业务代码一行不动。嗯,这才是架构该有的样子。

9.2 工厂方法模式:解析不同格式文件

再说文件解析。JSON、XML、CSV,这三种格式在数据交换中太常见了。每个格式的解析逻辑完全不同,但对外暴露的接口应该一致。

工厂方法模式的定义是:定义一个创建对象的接口,但让子类决定实例化哪个类。说白了,就是把「创建解析器」这件事延迟到子类。

9.2.1 解析器接口与具体实现

// 解析器接口
public interface FileParser {
    List<Map<String, Object>> parse(String content);
    boolean supports(String fileExtension);
}

// JSON 解析器
public class JsonFileParser implements FileParser {
    @Override
    public List<Map<String, Object>> parse(String content) {
        // 使用 Jackson 或 Gson 解析
        // 返回结构化数据
        return parsedData;
    }

    @Override
    public boolean supports(String fileExtension) {
        return "json".equalsIgnoreCase(fileExtension);
    }
}

// XML 解析器
public class XmlFileParser implements FileParser {
    @Override
    public List<Map<String, Object>> parse(String content) {
        // 使用 DOM 或 SAX 解析
        return parsedData;
    }

    @Override
    public boolean supports(String fileExtension) {
        return "xml".equalsIgnoreCase(fileExtension);
    }
}

// CSV 解析器
public class CsvFileParser implements FileParser {
    @Override
    public List<Map<String, Object>> parse(String content) {
        // 逐行解析,按逗号分割
        return parsedData;
    }

    @Override
    public boolean supports(String fileExtension) {
        return "csv".equalsIgnoreCase(fileExtension);
    }
}

9.2.2 工厂方法:创建解析器

public abstract class FileParserFactory {
    // 工厂方法,子类实现
    public abstract FileParser createParser();

    // 模板方法,定义解析流程
    public List<Map<String, Object>> parseFile(String fileName, String content) {
        FileParser parser = createParser();
        if (!parser.supports(getExtension(fileName))) {
            throw new IllegalArgumentException("不支持的文件格式: " + fileName);
        }
        return parser.parse(content);
    }

    private String getExtension(String fileName) {
        return fileName.substring(fileName.lastIndexOf('.') + 1);
    }
}

// 具体工厂
public class JsonFileParserFactory extends FileParserFactory {
    @Override
    public FileParser createParser() {
        return new JsonFileParser();
    }
}

public class XmlFileParserFactory extends FileParserFactory {
    @Override
    public FileParser createParser() {
        return new XmlFileParser();
    }
}

public class CsvFileParserFactory extends FileParserFactory {
    @Override
    public FileParser createParser() {
        return new CsvFileParser();
    }
}
我的经验: 工厂方法模式特别适合「解析器」这类场景。因为每个解析器的创建逻辑可能不同——比如 JSON 解析器可能需要配置日期格式,XML 解析器可能需要加载 DTD 文件。把这些差异封装在子类工厂里,主流程干干净净。

9.2.3 客户端使用:一行代码切换解析器

public class DataImporter {
    public void importData(String fileName, String content) {
        FileParserFactory factory = getFactory(fileName);
        List<Map<String, Object>> data = factory.parseFile(fileName, content);
        // 处理数据...
    }

    private FileParserFactory getFactory(String fileName) {
        if (fileName.endsWith(".json")) {
            return new JsonFileParserFactory();
        } else if (fileName.endsWith(".xml")) {
            return new XmlFileParserFactory();
        } else if (fileName.endsWith(".csv")) {
            return new CsvFileParserFactory();
        }
        throw new IllegalArgumentException("未知文件格式");
    }
}

你可能会问:这里不还是有 if-else 吗?没错,但这里的 if-else 只负责选择工厂,不负责解析逻辑。而且,这个 if-else 可以进一步优化——用注册表模式或者 SPI 机制,把工厂的注册完全解耦。我在一个数据中台项目里就是这么干的,新增一种文件格式,只需要加一个 jar 包,连配置都不用改。

9.3 抽象工厂 vs 工厂方法:怎么选?

这两个模式很容易搞混。我刚开始学的时候也迷糊过。后来总结了一个简单的判断标准:

维度 工厂方法 抽象工厂
创建对象数量 一个产品等级结构 多个产品等级结构(产品族)
典型场景 文件解析器、日志记录器 支付网关、UI 组件库
扩展方式 新增一个子类工厂 新增一个具体工厂(同时实现多个产品)
复杂度 较低 较高
避坑指南: 我曾经在一个项目中,把抽象工厂用在了只需要创建单一产品的场景。结果代码结构变得臃肿,每个工厂类里只有一个方法。后来重构为工厂方法,代码量直接砍半。记住:不要为了用模式而用模式,合适才是最好的。

9.4 本章知识体系

下面这张图,帮你把本章的核心逻辑串起来:

工厂模式实战知识体系 工厂方法模式 场景:解析不同格式文件 核心:创建单一产品等级结构 示例:JSON/XML/CSV 解析器 扩展:新增格式只需加子类工厂 抽象工厂模式 场景:支付网关多渠道对接 核心:创建多个产品等级结构(产品族) 示例:微信/支付宝/银联支付工厂 扩展:新增渠道需实现整套产品族 选择原则:单一产品用工厂方法,产品族用抽象工厂 避免过度设计,让模式服务于业务,而不是让业务迁就模式 对比

工厂模式的核心价值,就是把「创建」和「使用」分离。你想想看,如果哪天支付渠道变了,或者文件格式换了,你只需要动工厂那一块,业务逻辑完全不受影响。这就是架构的魅力。

好了,这一章就到这里。记住:模式是工具,不是目的。用对了,事半功倍;用错了,画蛇添足。


公众号:蓝海资料掘金营,微信deep3321