策略模式是一种行为型模式,核心是将不同的算法/业务逻辑封装成为独立的策略类,使它们可以相互替换,且策略的变化不影响使用策略的客户端。
代码实现
比如说在支付业务中,需要根据不同的支付方式处理支付结果。如以下代码所示:
public class PaymentService {
/**
* 统一支付方法:通过if-else分支区分支付方式
*/
public String pay(String payType, BigDecimal amount) {
// 校验金额(通用逻辑)
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
return "支付金额必须大于0!";
}
// 核心分支:不同支付方式的逻辑耦合在这里
if ("alipay".equals(payType)) {
// 支付宝支付逻辑(硬编码)
String alipayResult = "调用支付宝接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
// 支付宝特有逻辑:如签名验证、回调处理等
verifyAlipaySign();
return alipayResult;
} else if ("wxpay".equals(payType)) {
// 微信支付逻辑(硬编码)
String wxpayResult = "调用微信支付接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
// 微信特有逻辑:如appid校验、签名生成等
generateWxPaySign();
return wxpayResult;
} else if ("unionpay".equals(payType)) {
// 银联支付逻辑(硬编码)
String unionpayResult = "调用银联支付接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
// 银联特有逻辑:如证书校验等
verifyUnionPayCert();
return unionpayResult;
} else {
return "不支持的支付方式:" + payType;
}
}
// 通用工具方法:生成订单号
private String generateOrderNo() {
return "ORDER_" + System.currentTimeMillis();
}
// 支付宝特有方法
private void verifyAlipaySign() {
System.out.println("校验支付宝签名...");
}
// 微信支付特有方法
private void generateWxPaySign() {
System.out.println("生成微信支付签名...");
}
// 银联特有方法
private void verifyUnionPayCert() {
System.out.println("校验银联证书...");
}
}
class PaymentServiceTest {
public static void main(String[] args) {
PaymentService paymentService = new PaymentService();
// 支付宝支付
System.out.println(paymentService.pay("alipay", new BigDecimal("100")));
// 微信支付
System.out.println(paymentService.pay("wxpay", new BigDecimal("200")));
}
}
如果以后加入新的处理方式,这个支付实现类需要改变,且不利于扩展。如果出现组合支付,这个逻辑代码还要改动。根据设计原则:
找出应用中可能需要变化之处,把它们独立出来,不要和那些需要变化的代码混在一起
把变化的部分取出并“封装”起来,好让其他部分不受影响。代码变化引起的不经意后果变少,系统会变得更有弹性。所以将if代码块里面的逻辑抽出来,形成不同的支付方式,这就是把将来可能变化的代码独立出来。
独立出来的支付方法怎么设计呢,支付可以认为是一种行为方式,不同的支付方式则代表具体的行为。所以可以用接口代表每个行为,实现接口就是表示具体的行为。这里用到了一个设计原则:
针对接口编程,而不是针对实现编程
然后在支付实现类添加一个容器,配置具体支付策略对象,定义支付方法,支付实现类通过容器获取支付策略,如下面代码所示:
public class PaymentService {
private static final Map<String, PaymentStrategy> paymentStrategies = new HashMap<>();
static {
paymentStrategies.put("alipay", new AlipayStrategy());
paymentStrategies.put("wxpay", new WechatPayStrategy());
paymentStrategies.put("unionpay", new UnionPayStrategy());
}
/**
* 统一支付方法:通过if-else分支区分支付方式
*/
public String pay(String payType, BigDecimal amount) {
// 校验金额(通用逻辑)
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
return "支付金额必须大于0!";
}
PaymentStrategy paymentStrategy = paymentStrategies.get(payType);
// 核心分支:不同支付方式的逻辑耦合在这里
if (paymentStrategy != null) {
// 支付宝支付逻辑(硬编码)
return paymentStrategy.pay(amount);
} else {
return "不支持的支付方式:" + payType;
}
}
}
class PaymentServiceTest {
public static void main(String[] args) {
PaymentService paymentService = new PaymentService();
// 支付宝支付
System.out.println(paymentService.pay("alipay", new BigDecimal("100")));
// 微信支付
System.out.println(paymentService.pay("wxpay", new BigDecimal("200")));
// 银联支付
System.out.println(paymentService.pay("unionpay", new BigDecimal("300")));
// 不支持的支付方式
System.out.println(paymentService.pay("applepay", new BigDecimal("400")));
// 金额校验失败
System.out.println(paymentService.pay("alipay", new BigDecimal("0")));
}
}
interface PaymentStrategy {
String pay(BigDecimal amount);
String generateOrderNo();
void verifySign();
}
class WechatPayStrategy implements PaymentStrategy {
@Override
public String pay(BigDecimal amount) {
String wxpayResult = "调用微信支付接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
verifySign();
return wxpayResult;
}
@Override
public String generateOrderNo() {
return "ORDER_" + System.currentTimeMillis();
}
@Override
public void verifySign() {
System.out.println("生成微信支付签名...");
}
}
class AlipayStrategy implements PaymentStrategy {
@Override
public String pay(BigDecimal amount) {
String alipayResult = "调用支付宝接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
verifySign();
return alipayResult;
}
@Override
public String generateOrderNo() {
return "ORDER_" + System.currentTimeMillis();
}
@Override
public void verifySign() {
System.out.println("校验支付宝签名...");
}
}
class UnionPayStrategy implements PaymentStrategy {
@Override
public String pay(BigDecimal amount) {
// 银联支付逻辑(硬编码)
String unionpayResult = "调用银联支付接口,订单号:" + generateOrderNo()
+ ",支付金额:" + amount + "元 → 支付成功";
verifySign();
return unionpayResult;
}
@Override
public String generateOrderNo() {
return "ORDER_" + System.currentTimeMillis();
}
@Override
public void verifySign() {
System.out.println("校验银联证书...");
}
}
这样在我们新增支付方式时,就不需要改动支付实现类的代码了,只需要添加一个新的支付策略类实现接口方法。
将未来需要改变的和不改变的代码分离,并用实现而非继承的方式,这符合OO设计思想。后续可以添加策略工厂,降低策略对象的耦合。
核心结构(UML类图)
通过以上的例子对策略模式有了了解,现在再看策略模式的UML类图就容易理解了,如下:
-
Strategy(策略,如PaymentStrategy)
- 定义所有支持的算法的公共接口,如支付算法。Context使用这个接口来调用某个ConcreteStrategy 定义的算法。
-
ConcreteStrategy(具体策略,如WechatPayStrategy,AlipayStrategy)
- 以Strategy接口实现某个具体算法,如微信的支付算法,支付宝的支付算法。
-
Context(上下文,如PaymentService)
- 用一个ConcreteStrategy 对象来配置,如定义一个HashMap来具体算法对象。
- 定义一个接口来让Strategy访问它的数据。
- 维护一个对Strategy 对象的引用。
Strategy和Context 可以相互组合实现选定的算法,就像文中实现的支付策略一样。
框架中的应用
MyBatis的Interceptor接口是框架最重要的扩展点之一,框架实现了不同的拦截器,也可以自定义实现Interceptor,这就是策略模式的体现:
public interface Interceptor {
Object intercept(Invocation var1) throws Throwable;
Object plugin(Object var1);
void setProperties(Properties var1);
}
// 具体策略实现
@Component
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {...})
})
public class PerformanceMonitorInterceptor implements Interceptor {
// 性能监控策略
}
@Component
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {...})
})
public class DataPermissionInterceptor implements Interceptor {
// 数据权限策略
}
@Component
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {...})
})
public class PaginationInterceptor implements Interceptor {
// 分页策略
}
总结
如果说只有几种策略,并且未来是不会被改变的,那么可以选择不用策略模式。否则建议还是使用策略模式,它的优势在于:客户端可以动态的切换策略而无需改变代码、新增策略也只添加新的策略类不会改变原来代码、替换 if-else 或 switch 语句、策略类逻辑清晰职责单一。