策略(Strategy)这个词在不同领域都十分重要,在军事领域,策略关系到行军作战的方法,而在软件开发中,使用合适的策略能让我们的软件更加易于维护。我们常常能遇到某个功能存在多种不同的解决策略的问题。例如排序算法有冒泡排序、选择排序、插入排序、快速排序等方法可供选择。这就可以用到策略模式将算法封装成一个个的策略供使用者调用。策略模式可以整体地替换问题的实现部分,从而更加灵活地解决问题。
策略模式
策略模式(Strategy)的定义:定义一系列算法,将每个算法进行封装,使其能够灵活替换。当算法变化时,使用者无需更改系统的内部实现,这就是策略模式。策略模式属于行为型模式,在实际项目中应用较为广泛。
在合适的场景使用策略模式能够大量减少if else的产生,能够让你的项目具备更加良好的拓展性和可读性。
策略模式的结构
- 抽象策略(Strategy):定义一个公共接口,不同策略方法可以实现这个接口。同时这个接口将被上下文(Context)类进行调用,以决策不同算法模型。
- 具体策略(ConcreteStrategy):实现了抽象策略中定义的接口,提供具体实现。
- 上下文(Context):上下文类即环境类,它管理一个策略类(Strategy)的引用,客户端将调用Context提供的方法以访问策略模式。
策略模式的实际应用场景
- 支付场景:一个应用同时支持微信支付、支付宝支付、银行卡支付...这类场景可以使用策略模式实现。
- 生成唯一ID的场景:如UUID生成、雪花算法、用户Token生成...
- 购物时优惠券的发放:购物时通常优惠券会使用多种方式进行发放,最常见的就是直减优惠,也有满减优惠或者直接打折的方案。
在以上场景中,如果开发者都选择使用if else来判断用户选择的类型,那么程序将臃肿并且难以维护。所以使用策略模式能够很好地解决这些问题。
示例
接下来,我们以购物的优惠券发放作为例子详细讲解策略模式的实现。在本例中,我们将定义三种优惠券:直减优惠券,满减优惠券,打折优惠券。用户可以自行选择使用哪种优惠券进行购物。
首先,我们定义一个抽象策略类。抽象策略类中包含一个打折方法,以供具体策略类实现和上下文进行管理。
Strategy.java
package com.yeliheng.strategy;
/**
* 抽象策略
*/
public interface Strategy {
public long discount(long price); // 策略方法
}
接着我们定义三个具体策略类,分别是直减策略、满减策略、折扣策略。
直减策略类:
MinusStrategy.java
package com.yeliheng.strategy;
/**
* 直减策略
*/
public class MinusDiscount implements Strategy{
private final long amount = 100; // 直减100元
@Override
public long discount(long price) {
return price - amount;
}
}
直减策略类直接减去100元,返回最终价格。
满减策略类:
FullReductionDiscount.java
package com.yeliheng.strategy;
/**
* 满减策略
*/
public class FullReductionDiscount implements Strategy{
private final long count = 1000; //满1000
private final long amount = 100; //满减金额
@Override
public long discount(long price) {
if(price > count) {
return price - amount;
} else {
return price;
}
}
}
满减策略类中,对金额进行判断,若金额大于设定的1000元门槛,则减去满减金额100元,并返回最终价格。
折扣策略类:
BucklingDiscount.java
package com.yeliheng.strategy;
/**
* 折扣策略
*/
public class BucklingDiscount implements Strategy{
private final long discount = 8;
//为了演示简单,直接省去小数,使用long类型。实际项目切忌这样使用。
@Override
public long discount(long price) {
return (long) (price * discount * 0.1);
}
}
折扣策略类直接返回原价的8折。
完成了抽象策略类和具体策略类,我们定义Context类,即上下文类来对我们的策略环境进行管理。
Context.java
package com.yeliheng.strategy;
public class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void discount(long price) {
System.out.println("折后价格:" + strategy.discount(price));
}
}
在Context类中,提供了Strategy对象的set get 方法,并定义了discount打折方法,输出具体策略类计算后的价格。
最后,我们就可以在main函数中使用写好的策略模式了。
Main.java
package com.yeliheng.strategy;
public class Main {
private static final long price = 2000; // 商品价格
public static void main(String[] args) {
Context context = new Context();
System.out.println("[直减策略] 商品原价:" + price);
Strategy strategy = new MinusDiscount();
context.setStrategy(strategy);
context.discount(price);
System.out.println("-----------------");
System.out.println("[满减策略] 商品原价:" + price);
strategy = new FullReductionDiscount();
context.setStrategy(strategy);
context.discount(price);
System.out.println("-----------------");
System.out.println("[折扣策略] 商品原价:" + price);
strategy = new BucklingDiscount();
context.setStrategy(strategy);
context.discount(price);
}
}
在Main类中,我们依次对三种商品优惠券策略进行调用,运行结果如下图所示:
三种策略均正确输出。
在本例中,我们使用策略模式减少了if else的调用,使代码的逻辑更加清晰。什么时候选择什么策略,不再需要通过一堆逻辑判断语句,而是通过策略类交给一个上下文类来进行统一地管理。
策略模式的优缺点
优点
- 使用策略模式能大大减少多重条件语句的使用,优化逻辑结构,提升项目的可维护性。
- 策略模式提供一系列可复用的解决方法,能够减少重复代码的产生。
- 策略模式遵循开闭原则,能够在不修改原来代码的情况下增加新的策略。
- 策略模式能够灵活应对复杂算法场景下产生的逻辑问题。
- 策略模式使用一个Context上下文类进行策略环境的管理,有利于定义和实现的分离。
缺点
- 过度使用策略模式会造成策略类的增长,增加系统复杂度。
总结
策略模式是一种使用广泛的设计模式之一,正确理解和使用策略模式能够提升系统的可拓展性和可维护性。本文通过购物优惠券的应用示例详细讲解策略模式的应用。
本文示例的完整源代码参见:Github