大话设计模式读书笔记(二) 策略模式
书中通过一道题目,让"小菜"设计一个商场收银软件,营业员根据客户购买商品的数量及单价,想客户收费。,并且软件可以兼容打折,返点等优惠活动的金额计算。
一开始,小菜使用之前所学的简单工厂模式来制作这个收银软件。
收费父类
1 public abstract class CashSuper { 2 /** 3 * 收费方法 4 * @param money //原价 5 * @return 6 */ 7 public abstract double acceptCash(double money); 8 }
普通收费子类
1 /** 2 * 普通计算类 3 * 4 */ 5 public class CashNormal extends CashSuper{ 6 7 @Override 8 public double acceptCash(double money) { 9 10 return money; 11 } 12 13 }
打折收费类
1 //打折类 2 public class CashRebate extends CashSuper{ 3 4 private double cashRebate = 1d;//折扣 5 6 public CashRebate(double cashRebate) { 7 super(); 8 this.cashRebate = cashRebate; 9 } 10 @Override 11 public double acceptCash(double money) { 12 return money*cashRebate; 13 } 14 }
满减收费类
public class CashReturn extends CashSuper{ private double moneyCondition = 0.0d;//满多少钱开始反 private double moneyReturn = 0.0d;//反多少钱 public CashReturn(double moneyCondition, double moneyReturn) { super(); this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double money) { return money>moneyCondition? money-Math.floor(money/moneyCondition)*moneyReturn:money; } }
收费工厂类
1 //收费工厂 2 public class CashFactory { 3 public static CashSuper createCashSuper(String type){ 4 CashSuper cs = null; 5 switch (type) { 6 case "打8折": 7 cs=new CashRebate(0.8); 8 break; 9 case "满300减100": 10 cs=new CashReturn(300,100); 11 break; 12 default: 13 cs=new CashNormal(); 14 break; 15 } 16 return cs; 17 } 18 }
主方法
1 public class Main { 2 public static void main(String[] args) { 3 double totalPrice = 0.0d;//总金额 4 String type = "满300减100"; 5 CashSuper cs =CashFactory.createCashSuper(type); 6 double price = 400d;//未打折前金额 7 totalPrice+=cs.acceptCash(price);//打折后金额 8 System.out.println(totalPrice); 9 } 10 }
但是这里就遇到了我们在学习简单工厂方法时想到的问题,按照大鸟的话来讲;“简单工厂模式虽然也能解决这个问题,但是简单工厂模式只是解决对象的创建问题。而且由于工厂本身包含所有的收费方式,商场可能经常性的更改打折和满减方式,每次修改和扩展收费方式都要修改工厂类,以至于代码需要重新编译部署。这真的是很糟糕的处理方式,不是最好的方法。”
于是,引出了策略模式。。。。
策略模式(Strategy):它定义了算法家族,分别封装起来,让他们之间可以互相替代,此模式让算法的变化,不用影响使用算法的客户。
策略模式结构图
通过使用策略模式,修改代码,将打折,满减等算法,都有Context来配置,于是只需要加一个Context类和改一下主方法。
1 public class CashContext { 2 private CashSuper cashSuper;//收费接口 3 public CashContext(CashSuper cashSuper) { 4 this.cashSuper = cashSuper; 5 } 6 7 public double getResult(double money){ 8 return cashSuper.acceptCash(money); 9 } 10 }
主方法:
1 public class Main { 2 public static void main(String[] args) { 3 double totalPrice =0.0d;//用于小计 4 CashContext cc=null; 5 String type = "满300减100"; 6 switch (type) { 7 case "满300减100": 8 cc=new CashContext(new CashReturn(300, 100)); 9 break; 10 case "打9折": 11 cc=new CashContext(new CashRebate(0.9)); 12 break; 13 default: 14 cc=new CashContext(new CashNormal()); 15 break; 16 } 17 totalPrice+=cc.getResult(400); 18 System.out.println(totalPrice); 19 } 20 }
但是这样又需要客户端去判断到底执行什么算法。于是引出了策略模式与简单工厂模式的结合。
于是改造CashContext和主方法
1 public class CashContext { 2 private CashSuper cashSuper;//收费接口 3 public CashContext(String type) { 4 super(); 5 switch (type) { 6 case "满300减100": 7 cashSuper=new CashReturn(300, 100); 8 break; 9 case "打9折": 10 cashSuper=new CashRebate(0.9); 11 break; 12 default: 13 cashSuper=new CashNormal(); 14 break; 15 } 16 } 17 public double getResult(double money){ 18 return cashSuper.acceptCash(money); 19 } 20 }
主方法:
1 public class Main { 2 public static void main(String[] args) { 3 double totalPrice =0.0d;//用于小计 4 CashContext cc=null; 5 String type = "满300减100"; 6 cc = new CashContext(type); 7 totalPrice+=cc.getResult(400); 8 System.out.println(totalPrice); 9 } 10 }
对比简单工厂模式和策略模式与工厂模式的结合, 简单工厂模式需要客户端认识两个类CashSuper和CashFactory,而策略模式和工厂模式的结合,客户端只需要认识Context一个类就可以了,代码的耦合性进一步降低。
策略模式解析:
策略模式优点:
1、它可以以相同的方式来调用所有的方法,减少了各种算法类之间的耦合。例如上面例子中,无论是打折,还是满减,客户端调用的都是Context.getResult();。
2、简化了单元测试,每个算法都有自己的类,可以通过自己的接口进行单元测试。
3、当一个算法出现问题是,对其他算法没有影响。
策略模式应用场景:
只要在分析过程中听到有不同时间应用不同的业务规则,就可以考虑应用策略模式来处理这种变化。
策略模式的重心
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
策略模式的缺点
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
(2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。