1、传统的策略模式
在项目中,策略模式是一种常用的设计模式,核心由三部分构成:
- 接口(Strategy Interface):定义公共接口,规定每个策略类必须实现的方法,保证策略一致性;
- 实现类(Concrete Strategies):实现策略接口,封装具体的算法或行为;
- 上下文(Context):持有策略接口的引用,运行时动态切换和调用具体策略。
策略模式的优势在于,能将冗长的if-else/switch-case逻辑抽离到独立类中,让代码更清晰、易维护。新增或修改策略时,只需调整对应策略类,无需改动原有逻辑。
但它也有缺点:当条件分支较多时,对应的策略实现类会急剧增加,可能导致“类爆炸”,增加代码管理难度。
那么,若想抽离条件逻辑,又不想把逻辑封装在专门的策略类中,而是直接复用任意类的方法,该怎么做呢?
2、Map+函数式接口实战
借助Java 8的函数式接口(如Function)和Map,可实现更灵活的策略模式,避免类爆炸问题。核心代码如下:
private final Map<String, Function<T, R>> strategyMap = new HashMap<>();
其中,Map的key是判断条件,value是对应的执行逻辑;Function的泛型T是方法入参类型,R是返回值类型。
业务场景
电商订单支付时,需根据用户选择的支付方式(如支付宝、微信、银联)调用不同接口处理支付。每种支付方式有独立的接口和逻辑,我们希望以“支付方式类型(payType)”为条件,根据订单信息调用对应接口完成支付。
代码实现
第一步:定义基础类和DTO
先创建订单和支付结果的载体类,用于传递数据:
// 订单信息DTO
@Data
public class OrderDTO {
private String orderNo; // 订单编号
private BigDecimal amount; // 支付金额
private String userId; // 用户ID
// 其他订单相关字段...
}
// 支付结果DTO
@Data
public class PaymentResultDTO {
private boolean success; // 支付是否成功
private String orderNo; // 订单编号
private String transactionId; // 第三方交易号
private String payMethod; // 支付方式
// 其他支付结果字段...
}
第二步:实现支付服务类
不同支付方式的具体逻辑,封装在各自的服务类中:
// 支付宝支付服务
@Service
public class AlipayService {
/**
* 支付宝支付处理
* @param order 订单信息
* @return 支付结果
*/
public PaymentResultDTO processAlipay(OrderDTO order) {
// 调用支付宝接口处理支付(模拟)
System.out.println("调用支付宝接口,订单" + order.getOrderNo() + "支付中...");
PaymentResultDTO result = new PaymentResultDTO();
result.setSuccess(true);
result.setOrderNo(order.getOrderNo());
result.setPayMethod("支付宝");
result.setTransactionId("ALIPAY_" + System.currentTimeMillis());
return result;
}
}
// 微信支付服务
@Service
public class WechatPayService {
/**
* 微信支付处理
* @param order 订单信息
* @return 支付结果
*/
public PaymentResultDTO processWechatPay(OrderDTO order) {
// 调用微信支付接口处理支付(模拟)
System.out.println("调用微信支付接口,订单" + order.getOrderNo() + "支付中...");
PaymentResultDTO result = new PaymentResultDTO();
result.setSuccess(true);
result.setOrderNo(order.getOrderNo());
result.setPayMethod("微信支付");
result.setTransactionId("WECHAT_" + System.currentTimeMillis());
return result;
}
}
// 银联支付服务
@Service
public class UnionPayService {
/**
* 银联支付处理
* @param order 订单信息
* @return 支付结果
*/
public PaymentResultDTO processUnionPay(OrderDTO order) {
// 调用银联接口处理支付(模拟)
System.out.println("调用银联接口,订单" + order.getOrderNo() + "支付中...");
PaymentResultDTO result = new PaymentResultDTO();
result.setSuccess(true);
result.setOrderNo(order.getOrderNo());
result.setPayMethod("银联支付");
result.setTransactionId("UNIONPAY_" + System.currentTimeMillis());
return result;
}
}
第三步:用Map+函数式接口整合策略
创建支付上下文类,通过Map存储“支付方式-处理逻辑”的映射,替代if-else:
@Service
public class PaymentContext {
// 存储支付方式与对应处理逻辑的映射:key=支付方式类型,value=处理方法
private final Map<Integer, Function<OrderDTO, PaymentResultDTO>> payStrategyMap = new HashMap<>();
// 注入各支付服务
@Resource
private AlipayService alipayService;
@Resource
private WechatPayService wechatPayService;
@Resource
private UnionPayService unionPayService;
// 初始化映射关系(替代if-else)
public PaymentContext() {
// 支付宝(支付类型1)
payStrategyMap.put(1, order -> alipayService.processAlipay(order));
// 微信支付(支付类型2)
payStrategyMap.put(2, order -> wechatPayService.processWechatPay(order));
// 银联支付(支付类型3)
payStrategyMap.put(3, order -> unionPayService.processUnionPay(order));
}
// 根据支付类型处理支付
public PaymentResultDTO processPayment(Integer payType, OrderDTO order) {
// 从map中获取对应策略
Function<OrderDTO, PaymentResultDTO> payFunction = payStrategyMap.get(payType);
// 执行策略并返回结果(处理未匹配的支付类型)
return Optional.ofNullable(payFunction)
.map(func -> func.apply(order))
.orElseThrow(() -> new IllegalArgumentException("不支持的支付方式:" + payType));
}
}
核心逻辑说明
- 用
payStrategyMap存储“支付类型-处理方法”的映射:key是支付类型(1=支付宝、2=微信、3=银联),value是对应的处理方法(如alipayService::processAlipay)。 - 当需要处理支付时,只需通过
payType从map中获取对应方法,直接调用即可,无需if-else判断。
3、总结
相比传统策略模式,Map+函数式接口的实现有明显优势:
- 避免类爆炸:无需为每个策略创建独立类,直接复用现有服务类的方法;
- 代码更简洁:用map的key-value映射替代冗长的if-else,逻辑更清晰;
- 扩展性更强:新增支付方式时,只需添加服务类方法,并在map中注册,无需修改原有代码;
- 维护更方便:所有策略映射集中在map初始化处,便于统一管理。
通过使用 Map 和函数式接口,我们可以更加灵活地实现策略模式,避免了传统策略模式中因大量 if-else 或 switch-case 语句而导致的代码复杂性。这种方法不仅使代码更加简洁和可读,还提高了代码的可扩展性和可维护性。