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();
}
}
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 本章知识体系
下面这张图,帮你把本章的核心逻辑串起来:
工厂模式的核心价值,就是把「创建」和「使用」分离。你想想看,如果哪天支付渠道变了,或者文件格式换了,你只需要动工厂那一块,业务逻辑完全不受影响。这就是架构的魅力。
好了,这一章就到这里。记住:模式是工具,不是目的。用对了,事半功倍;用错了,画蛇添足。