二、策略设计模式
策略设计模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略设计模式使得算法可以在不影响到客户端的情况下发生变化。
策略设计模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。
1、优点:
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
- 使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
2、缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
- 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
3、应用场景举例:
- 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
- 策略模式在Java中的应用很广泛,如Comparator或者Comparable接口就是使用了策略设计模式。如对List集合中的元素进行排序时,对于不同的元素类型传入不同的Comparator实现类来进行元素的比较从而实现对List集合的排序。注意区分Comparable和Comparator接口的区别。在Comparable接口中,e1.compareTo(e2) == 0的布尔值应该等价于e1.equals(e2)。而对于Comparator接口则不需要严格保证这点。
4、代码实现:
以超市购物结算为例,在购物时,若当前用户是会员,则会进行9折优惠。若商品有优惠,如满300减100的活动。不是会员并且商品无活动,则按照原价计算。这里对于用户购买商品的计算分为多种,因此可以采用策略设计模式将每一种结算方式进行封装。
Strategy类:
/**
* 超市购物金额结算接口
*/
public interface CalculateCash {
double getFinalCash(double money);
}
ConcreteStrategy类:
/**
* 普通结算,不打折
*/
public class CalculateCash_Normal implements CalculateCash {
@Override
public double getFinalCash(double money) {
return money;
}
}
/**
* 结算时可以进行打折
*/
public class CalculateCash_Rebate implements CalculateCash {
private double rebate; // 打折率
public CalculateCash_Rebate(double rebate) {
this.rebate = rebate;
}
@Override
public double getFinalCash(double money) {
return money * rebate;
}
}
/**
* 满减活动,如满300减100等活动
*/
public class CalculateCash_Return implements CalculateCash {
private double moneyCondition; // 满减条件
private double moneyReturn; // 满减金额
public CalculateCash_Return(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double getFinalCash(double money) {
double result = 0;
// 如果当前金额满足满减条件,则需要减去满减金额
if (Double.compare(money, moneyCondition) >= 0) {
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
Context类:
/**
* 客户端,根据传入的参数来选择不同的结算算法
*/
public class CashContext {
private CalculateCash calculateCash;
public double getFinalCash(double money) {
return calculateCash.getFinalCash(money);
}
/**
* 设置结算算法
*/
public void setCalculateCash(String type) {
switch (type) {
case "正常收费" :
calculateCash = new CalculateCash_Normal();
break;
case "满300减100" :
calculateCash = new CalculateCash_Return(300,
100);
break;
case "打八折" :
calculateCash = new CalculateCash_Rebate(0.8);
break;
}
}
}
测试类:
/**
* 测试类
*/
public class App {
public static void main(String[] args) {
CashContext cashContext = new CashContext();
cashContext.setCalculateCash("正常收费");
System.out.println(cashContext.getFinalCash(100));
cashContext.setCalculateCash("打八折");
System.out.println(cashContext.getFinalCash(100));
cashContext.setCalculateCash("满300减100");
System.out.println(cashContext.getFinalCash(400));
}
}