策略模式,从语义上可以看出,这种模式是针对于有多种策略的场景,
例如商场促销商品,按照消费金额,有三种优惠策略:
- 满100减20
- 满200减50
- 满300减100
那有的同学就说了,很简单啊,就直接 if else 判断消费金额不就完事了。当然这肯定也能满足需求,那如果后期再新增别的策略,办理会员优惠后再打折,你就不能直接判断消费金额了,你需要看当前消费者是不是会员,是会员然后看看满足哪种优惠策略,这么一套下来,你的代码可能写到最后,各种 if else 判断,可能你自己都不想去看了,而且最重要的一点是不符合开闭原则。
那如果你使用策略模式,各种策略都是单独的存在,单从代码阅读性和扩展性上,会好很多
策略模式涉及到三个角色:
- 环境角色,持有一个策略Strategy的引用
- 抽象策略角色,这是一个抽象角色,通常由一个接口或抽象类实现,此角色给出所有具体策略类所需的接口
- 具体策略角色,包装了相关算法或行为
UML:
代码演示:
/**
* 1、抽象优惠策略
*/
public interface DiscountsStrategy {
/**
* 普通优惠
*
* @param amount 消费金额
* @return
*/
Double discounts(Double amount);
/**
* 会员打折
*
* @param amount 消费金额
* @param memberDiscounts 会员折扣
* @return
*/
Double discounts(Double amount, Double memberDiscounts);
}
/**
* 2、策略实现类
*/
public class StrategyImpl implements DiscountsStrategy {
/**
* 普通优惠
*/
@Override
public Double discounts(Double amount) {
// 子类重写
return null;
}
/**
* 会员优惠,普通优惠后再打折
*/
@Override
public Double discounts(Double amount, Double memberDiscounts) {
return discounts(amount) * memberDiscounts;
}
}
/**
* 3、满100减20
*/
public class Strategy1 extends StrategyImpl {
@Override
public Double discounts(Double amount) {
if (amount >= 100) {
return amount - 20;
}
return amount;
}
}
/**
* 4、满200减50
*/
public class Strategy2 extends StrategyImpl {
@Override
public Double discounts(Double amount) {
if (amount >= 200) {
return amount - 50;
}
return amount;
}
}
/**
* 5、满300减100
*/
public class Strategy3 extends StrategyImpl {
@Override
public Double discounts(Double amount) {
if (amount >= 300) {
return amount - 100;
}
return amount;
}
}
/**
* 6、策略环境
*/
public class StrategyContext {
/**
* 抽象策略
*/
private DiscountsStrategy strategy;
/**
* 消费金额
*/
private Double amount;
/**
* 会员折扣
*/
private Double memberDiscounts;
/**
* 会员构造函数
* 这里可以思考一下,我还想再增加个属性,那是不是就要改,或者再新增一个构造函数?
* 怎么规避这种繁琐的操作?当然有办法,建造者模式,下篇说这个模式
*/
public StrategyContext(DiscountsStrategy strategy, Double amount, Double memberDiscounts) {
this.strategy = strategy;
this.amount = amount;
this.memberDiscounts = memberDiscounts;
}
/**
* 普通构造函数
*/
public StrategyContext(DiscountsStrategy strategy, Double amount) {
this.strategy = strategy;
this.amount = amount;
}
/**
* 计算优惠金额
*
* @return
*/
public Double executeStrategy() {
if (memberDiscounts != null) {
// 会员
return strategy.discounts(amount, memberDiscounts);
} else {
// 非会员
return strategy.discounts(amount);
}
}
}
public class DiscountsAmount {
public static void main(String[] args) {
// 定义策略
DiscountsStrategy strategy = new Strategy2();
// 初始化策略环境
StrategyContext context = new StrategyContext(strategy,200.0, 0.8);
// 计算优惠后金额
Double amount = context.executeStrategy();
System.out.println("优惠后金额:" + amount);
}
}
在现实生活中很少会有这种每个策略单独存在,通常会让这三种策略同时存在,然后按照消费金额,去匹配相应的优惠策略,那我们再增加一个策略,让这个策略包含上面这三种策略
/**
* 7、
* 满100减20
* 满200减50
* 满300减100
*/
public class Strategy4 extends StrategyImpl {
@Override
public Double discounts(Double amount) {
if (amount >= 100 && amount < 200) {
return new Strategy1().discounts(amount);
} else if (amount >= 200 && amount < 300) {
return new Strategy2().discounts(amount);
} else if (amount > 300) {
return new Strategy3().discounts(amount);
}
return amount;
}
}
public class DiscountsAmount {
public static void main(String[] args) {
// 定义策略,这里只需要更换策略即可
DiscountsStrategy strategy = new Strategy4();
// 初始化策略环境
StrategyContext context = new StrategyContext(strategy,200.0, 0.8);
// 计算优惠后金额
Double amount = context.executeStrategy();
System.out.println("优惠后金额:" + amount);
}
}
策略模式的优缺点
优点
1、避免了多重条件if...else if...else语句,多重条件语句并不容易维护
2、策略模式提供了管理相关算法簇的办法,恰当使用继承可以把公共代码移到父类,从而避免了代码重复
缺点
1、客户端必须知道所有的策略类,并自行决定使用 哪一个策略,这意味着客户端必须理解这些算法的区别,以便选择恰当的算法
2、如果备选策略很多,就会有很多策略类