文章目录
Talk is cheap, show me the code.
背景
项目中要用对接 10 家银行的支付接口、余额查询、流水交易结果查询等接口
作者将尝试使用策略模式 + 门面模式
设计一下API,增加 API 的易用性
一、定义银行操作的统一接口(策略接口)
// 1. 定义银行操作的统一接口(策略接口)
public interface BankOperations {
PaymentResult doPayment(PaymentRequest request);
BalanceResult queryBalance(BalanceRequest request);
TransactionResult queryTransaction(TransactionRequest request);
}
二、各银行实现类
工商银行
// 2. 各银行实现类
public class ICBCBankOperations implements BankOperations {
@Override
public PaymentResult doPayment(PaymentRequest request) {
// 工商银行支付实现
return null;
}
@Override
public BalanceResult queryBalance(BalanceRequest request) {
// 工商银行余额查询实现
return null;
}
@Override
public TransactionResult queryTransaction(TransactionRequest request) {
// 工商银行交易查询实现
return null;
}
}
农业银行
public class ABCBankOperations implements BankOperations {
// 农业银行实现...
}
建设银行
public class CCBBankOperations implements BankOperations {
// 建设银行实现...
}
三、银行类型枚举
// 3. 银行类型枚举
public enum BankType {
ICBC, ABC, CCB
}
四、统一的请求和响应对象
// 4. 统一的请求和响应对象
@Data
public class PaymentRequest {
private String accountNo;
private BigDecimal amount;
private String currency;
private String purpose;
// 其他通用支付参数...
}
@Data
public class PaymentResult {
private String transactionId;
private String status;
private String message;
// 其他通用返回字段...
}
五、银行操作工厂
// 5. 银行操作工厂
@Component
public class BankOperationsFactory {
private final Map<BankType, BankOperations> operationsMap = new HashMap<>();
@Autowired
public BankOperationsFactory(List<BankOperations> operations) {
// 初始化银行操作实现映射
operations.forEach(operation -> {
if (operation instanceof ICBCBankOperations) {
operationsMap.put(BankType.ICBC, operation);
} else if (operation instanceof ABCBankOperations) {
operationsMap.put(BankType.ABC, operation);
} else if (operation instanceof CCBBankOperations) {
operationsMap.put(BankType.CCB, operation);
}
});
}
public BankOperations getOperation(BankType bankType) {
return operationsMap.get(bankType);
}
}
六、门面类 - 提供统一的接口访问点
// 6. 门面类 - 提供统一的接口访问点
@Service
public class BankServiceFacade {
private final BankOperationsFactory factory;
@Autowired
public BankServiceFacade(BankOperationsFactory factory) {
this.factory = factory;
}
public PaymentResult doPayment(BankType bankType, PaymentRequest request) {
try {
BankOperations operations = factory.getOperation(bankType);
return operations.doPayment(request);
} catch (Exception e) {
log.error("Payment failed", e);
return PaymentResult.builder()
.status("FAILED")
.message(e.getMessage())
.build();
}
}
public BalanceResult queryBalance(BankType bankType, BalanceRequest request) {
try {
BankOperations operations = factory.getOperation(bankType);
return operations.queryBalance(request);
} catch (Exception e) {
log.error("Balance query failed", e);
return BalanceResult.builder()
.status("FAILED")
.message(e.getMessage())
.build();
}
}
public TransactionResult queryTransaction(BankType bankType, TransactionRequest request) {
try {
BankOperations operations = factory.getOperation(bankType);
return operations.queryTransaction(request);
} catch (Exception e) {
log.error("Transaction query failed", e);
return TransactionResult.builder()
.status("FAILED")
.message(e.getMessage())
.build();
}
}
}
七、使用示例
// 7. 使用示例
@Service
public class PaymentService {
@Autowired
private BankServiceFacade bankService;
public void processPayment() {
PaymentRequest request = new PaymentRequest();
request.setAccountNo("1234567890");
request.setAmount(new BigDecimal("100.00"));
// 调用工商银行支付
PaymentResult result = bankService.doPayment(BankType.ICBC, request);
// 处理支付结果...
}
}
设计说明
策略模式:
- BankOperations 接口定义了所有银行操作的统一接口
- 每个银行都有自己的实现类(ICBCBankOperations, ABCBankOperations, CCBBankOperations)
- 通过工厂类动态选择具体的实现
门面模式:
- BankServiceFacade 提供了一个统一的访问点
- 封装了异常处理和日志记录
- 简化了客户端的调用方式
扩展性:
- 添加新银行只需要:
- 实现 BankOperations 接口
- 在 BankType 中添加新的枚举值
- 在工厂类中注册新的实现
- 添加新银行只需要:
可维护性:
- 每个银行的实现相互独立
- 共用的代码抽取到基类或工具类
- 统一的异常处理和日志记录
使用建议:
- 可以添加配置类管理银行的连接参数
- 可以使用适配器模式处理不同银行的返回格式
- 可以添加重试机制和熔断机制
- 建议添加参数验证和安全检查
这样的设计让代码结构清晰,易于维护和扩展,同时也提供了良好的用户体验。