本文来自廖雪峰,郎涯进行简单排版与补充
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
策略模式,即Strategy,是为了允许调用方选择一个算法,从而通过不同策略实现不同的计算结果。
通过扩展策略,不必修改主逻辑,即可获得新策略的结果。
策略模式在Java标准库中应用非常广泛,我们以排序为例,看看如何通过Arrays.sort()
实现忽略大小写排序:
Arrays.sort(array, String::compareToIgnoreCase);
如果我们想忽略大小写排序,就传入String::compareToIgnoreCase
,如果我们想倒序排序,就传入(s1, s2) -> -s1.compareTo(s2)
,这个比较两个元素大小的算法就是策略。
一个完整的策略模式要定义策略以及使用策略的上下文。我们以购物车结算为例,假设网站针对普通会员、Prime会员有不同的折扣,同时活动期间还有一个满100减20的活动,这些就可以作为策略实现。先定义打折策略接口:
public interface DiscountStrategy {
// 计算折扣额度:
BigDecimal getDiscount(BigDecimal total);
}
接下来,就是实现各种策略。普通用户策略如下:
public class UserDiscountStrategy implements DiscountStrategy {
public BigDecimal getDiscount(BigDecimal total) {
// 普通会员打九折:
return total.multiply(new BigDecimal("0.1")).setScale(2, RoundingMode.DOWN);
}
}
满减策略如下:
public class OverDiscountStrategy implements DiscountStrategy {
public BigDecimal getDiscount(BigDecimal total) {
// 满100减20优惠:
return total.compareTo(BigDecimal.valueOf(100)) >= 0 ? BigDecimal.valueOf(20) : BigDecimal.ZERO;
}
}
最后,要应用策略,我们需要一个DiscountContext
:
public class DiscountContext {
// 持有某个策略:
private DiscountStrategy strategy = new UserDiscountStrategy();
// 允许客户端设置新策略:
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal calculatePrice(BigDecimal total) {
return total.subtract(this.strategy.getDiscount(total)).setScale(2);
}
}
调用方必须首先创建一个DiscountContext,并指定一个策略(或者使用默认策略),即可获得折扣后的价格:
DiscountContext ctx = new DiscountContext();
// 默认使用普通会员折扣:
BigDecimal pay1 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.println(pay1);
// 使用满减折扣:
ctx.setStrategy(new OverDiscountStrategy());
BigDecimal pay2 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.println(pay2);
// 使用Prime会员折扣:
ctx.setStrategy(new PrimeDiscountStrategy());
BigDecimal pay3 = ctx.calculatePrice(BigDecimal.valueOf(105));
System.out.println(pay3);
上述完整的策略模式如下图所示:
┌───────────────┐ ┌─────────────────┐
│DiscountContext│─ ─ ─>│DiscountStrategy │
└───────────────┘ └─────────────────┘
▲
│ ┌─────────────────────┐
├─│UserDiscountStrategy │
│ └─────────────────────┘
│ ┌─────────────────────┐
├─│PrimeDiscountStrategy│
│ └─────────────────────┘
│ ┌─────────────────────┐
└─│OverDiscountStrategy │
└─────────────────────┘
策略模式的核心思想是在一个计算方法中把容易变化的算法抽出来作为“策略”参数传进去,从而使得新增策略不必修改原有逻辑。