策略模式Strategy

第二章 商场促销-策略模式

1.1简单工厂实例
现在我们需要为某大型超市设计一个收银软件,收银员根据顾客购买的商品的单价和数量,向顾客收费
根据我们之前学到的简单工厂模式,我们可以这样做
代码结构图如下:
抽象产品Product角色CashSuper
package dp02Strategy;
public abstract class CashSuper {
     //现金收取超类的抽象方法,收取现金,参数为原价,返回当前价
     public abstract double acceptCash(double money);
}

 

 
具体Product角色之正常收费类CashNormal
package dp02Strategy;
public class CashNormal extends CashSuper{
     public double acceptCash(double money) {
          return money;
     }
}
 
具体Product角色之打折收费类CashRebate
package dp02Strategy;
public class CashRebate extends CashSuper{
     //暂定打折率为1
     private double moneyRebate=1d;
     CashRebate(double moneyRebate){
          this.moneyRebate=moneyRebate;
     }
     public double acceptCash(double money) {
          return money*moneyRebate;
     }
 
}

 

具体Product角色之返利收费类,例如满400减50
package dp02Strategy;
public class CashReturn extends CashSuper{
     //返利条件
     private double returnCondition=0d;
     //返利值
     private double returnMoney=0d;
     CashReturn(double returnCondition, double returnMoney) {
          this.returnCondition = returnCondition;
          this.returnMoney = returnMoney;
     }
     public double acceptCash(double money) {
          double result=money;
          if(money>returnCondition){
               result=money-Math.floor(money/returnCondition)*returnMoney;
          }
          return result;
     }
 
}

 

收费对象工厂CashFactory
package dp02Strategy;
public class CashFactory {
     public static CashSuper createCashSuper(String type) {
          CashSuper cs=null;
          switch (type) {
          case "正常收费":
              cs=new CashNormal();
              break;
          case "打8折":
              cs=new CashRebate(0.8);
              break;
          case "满300减100":
              cs= new CashReturn(400, 50);
              break;
          }
          return cs;
     }
}

 

测试类Cashier
package dp02Strategy;
public class Cashier {
     public static void main(String[] args) {
          CashSuper cs1 = CashFactory.createCashSuper("正常收费");
          double cash = cs1.acceptCash(100);
          System.out.println(cash);
          System.out.println("-----------------------------------");
          CashSuper cs2 = CashFactory.createCashSuper("满400减50");
          double cash2 = cs2.acceptCash(700);
          System.out.println(cash2);
          System.out.println("-----------------------------------");
          CashSuper cs3 = CashFactory.createCashSuper("打8折");
          double cash3 = cs3.acceptCash(700);
          System.out.println(cash3);
     }
}
 
测试结果
 
通过以上代码实现,我们发现简单工厂模式虽然也能解决这个问题,但是这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,超市是可能经常性的更改打折额度和返利额度的,每次维护或者扩展收费方式都要改动这个工厂,以致代码需要重新编译部署,违反了开放-封闭原则;这真的是很糟糕的处理方式,所以用它不是最好的方法.面对算法的时常变动(产品的需求改动),应该使用更好的办法,即策略模式
 
2.1什么是策略模式?
策略模式 Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户
 
超市收银时如何促销,用打折还是返利,其实本质上都是一些算法,用工厂来生成算法对象,这没有错,但是算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式.
策略模式的UML图如下
 
Strategy类,定义所有支持的算法的公共接口
package dp02Strategy;
/**
 *
 * @ClassName: Strategy
 * @Description: 策略类,定义所有支持的算法的公共接口
 * @author lizhaowen
 * @date 2017年4月12日 上午11:05:21
 * 策略模式Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相
 * 替换,此模式让算法的的变化,不会影响到使用算法的客户
 */
public abstract class Strategy {
     //算法方法
     public abstract void algorithmInterface();
}

ConcreteStrategy,封装了具体的算法或者行为,它继承于Strategy

package dp02Strategy;
 
public class ConcreteStrategyA extends Strategy{
 
     //算法A实现方法
     public void algorithmInterface() {
          System.out.println("算法A实现");
     }
 
}
 
package dp02Strategy;
 
public class ConcreteStrategyB extends Strategy{
 
     //算法B实现方法
     public void algorithmInterface() {
          System.out.println("算法B实现");
     }
 
}
 
package dp02Strategy;
 
public class ConcreteStrategyC extends Strategy{
 
     //算法C实现方法
     public void algorithmInterface() {
          System.out.println("算法C实现");
     }
 
}
 
Context上下文,我们使用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
package dp02Strategy;
/**
 *
 * @ClassName: Context
 * @Description: Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
 * @author lizhaowen
 * @date 2017年4月12日 上午11:17:10
 *
 */
public class Context {
     Strategy strategy;
 
     public Context(Strategy strategy) {//初始化时,传入具体的的策略对象
          this.strategy = strategy;
     }
     //上下文接口
     public void ContextInterface(){
          //根据具体的策略对象,调用其算法的方法
          strategy.algorithmInterface();
     }
}
 
测试类StrategyClient
package dp02Strategy;
 
public class StrategyClient {
     public static void main(String[] args) {
          // 由于实例化不同的策略,所以最终在调用context.ContextInterface();时,所获得的结果就不尽相同
          Context context;
          context = new Context(new ConcreteStrategyA());
          context.ContextInterface();
 
          context = new Context(new ConcreteStrategyB());
          context.ContextInterface();
 
          context = new Context(new ConcreteStrategyC());
          context.ContextInterface();
     }
}
 
对照一下我们使用简单工厂实现的功能代码,CashSuper就是抽象策略,而正常收费CashNormal 打折收费CashRebate 和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法.我们现在只需要添加一个CashContext类,并修改一下测试类就OK了!
代码结构图
CashContext类
package dp02Strategy02;
 
public class CashContext {
     CashSuper cs;// 声明一个{@see CashSuper}对象
 
     // 注意参数不是具体的收费类策略对象,而是一个字符串,表示收费类型
     public CashContext(String type) {
          //将实例化具体策略的过程由客户端转移到Context中,这是简单工厂的应用
          switch (type) {
          case "正常收费":
              cs = new CashNormal();
              break;
          case "满300返100":
              cs = new CashReturn(300, 100);
              break;
          case "打5折":
              cs = new CashRebate(0.5);
              break;
          }
     }
 
     // 上下文接口
     public double getResult(double money) {
          return cs.acceptCash(money);
     }
 
}

 

测试类
package dp02Strategy02;
 
public class CashClient {
     public static void main(String[] args) {
          double totalPrices=0d;
          CashContext context = new CashContext("正常收费");
          totalPrices = context.getResult(100);
          System.out.println(totalPrices);
     }
 
} 
对比简单工厂模式与策略模式的客户端代码
 
 //简单工厂模式的用法
CashSuper cs1 = CashFactory.createCashSuper("正常收费");
 
 
//策略模式与简单工厂模式结合的用法
CashContext context = new CashContext("正常收费");
 
 
 
经过对比,我们发现SimpleFactory我们需要让测试类认识两个类CashSuper和CashFactory,而Strategy与SimpleFactory相结合的用法,客户端只需要认识一个类CashContext就可以了,更加降低了耦合度

2.2解析策略模式

策略模式是一种定义了一系列算法的方法,从概念(策略模式 Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户 )上看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
 
其优点有:
优点其一,策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或者行为,继承有助于析取这些算法中的公共功能,在本例中公共的功能是获得计算费用的结果getResult,这使得算法间有了抽象的父类CashSuper.
优点其二,简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
优点其三,我们在最开始编程时,不得不在测试类中为了判断使用哪一个算法而用了switch条件分支,这是正常的,因为当不同的行为堆彻在一个类中,就难免使用条件语句来选择合适的行为,将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句,就超市收银系统的例子而言,在测试类中就消除条件语句,避免了大量的判断,这是非常重要的进展.我们用一句话概括之:策略模式封装了变化.
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性.
posted @ 2017-04-12 17:04  李昭文-Java成长之路  阅读(206)  评论(0编辑  收藏  举报