【设计模式】策略模式+门面模式设计对接银行接口的API

发布于:2025-03-29 ⋅ 阅读:(24) ⋅ 点赞:(0)

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);
        
        // 处理支付结果...
    }
}

设计说明

  1. 策略模式

    • BankOperations 接口定义了所有银行操作的统一接口
    • 每个银行都有自己的实现类(ICBCBankOperations, ABCBankOperations, CCBBankOperations)
    • 通过工厂类动态选择具体的实现
  2. 门面模式

    • BankServiceFacade 提供了一个统一的访问点
    • 封装了异常处理和日志记录
    • 简化了客户端的调用方式
  3. 扩展性

    • 添加新银行只需要:
      1. 实现 BankOperations 接口
      2. 在 BankType 中添加新的枚举值
      3. 在工厂类中注册新的实现
  4. 可维护性

    • 每个银行的实现相互独立
    • 共用的代码抽取到基类或工具类
    • 统一的异常处理和日志记录
  5. 使用建议

    • 可以添加配置类管理银行的连接参数
    • 可以使用适配器模式处理不同银行的返回格式
    • 可以添加重试机制和熔断机制
    • 建议添加参数验证和安全检查

这样的设计让代码结构清晰,易于维护和扩展,同时也提供了良好的用户体验。