定义:
定义一系列的算法,把它们一个个封装起来到一个对象中,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化,在运行时灵活的使用其中的一个算法。
在一个方法中,流程是确定的,但是,某些关键步骤的算法依赖调用方传入的策略,这样,传入不同的策略,即可获得不同的结果,大大增强了系统的灵活性。
图示:
Strategy接口: 定义每个策略或算法必须具有的方法和属性。
ConcreteStrategy类: 具体策略的实现;
Context类: 上下文角色,有承上启下封装作用,负责和具体的策略实现交互。
举例实现:
假如我们去商店购物,该商店今日有活动满100减20,并且对于普通用户有折扣,此时我们就可以用策略模式解决购物时选择何种优惠方案的问题。
第一步:定义Strategy接口;
public interface DiscountStrategy {
// 计算折扣额度:
BigDecimal getDiscount(BigDecimal total);
}
第二步:实现各种策略(ConcreteStrategy类);
//普通用户策略如下:
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;
}
}
第三步:应用策略,我们需要一个Context类;
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);
至此,我们的策略模式的例子就完成了。
使用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:
如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
总结:
策略模式遵循了开闭原则,增加新的类不需要修改原有的代码,只需实现接口或者继承抽象类;同时策略模式也遵循了里氏替换原则,具体的策略类都有相同的接口,只要在有父类出现的地方都可以使用子类替代。