zoukankan      html  css  js  c++  java
  • 策略模式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类中,可以在使用这些行为的类中消除条件语句,就超市收银系统的例子而言,在测试类中就消除条件语句,避免了大量的判断,这是非常重要的进展.我们用一句话概括之:策略模式封装了变化.
    策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性.
  • 相关阅读:
    list接口如何使用
    分页导航jsp
    jstl遍历list的jsp
    sql分页查询
    sql计算总页数
    类和对象,类定义了对象的特征和行为。属性,方法。
    编写一个带有main函数的类,调用上面的汽车类,实例化奔驰、大众、丰田等不同品牌和型号,模拟开车过程:启动、加速、转弯、刹车、息火,实时显示速度。
    java编写一个汽车类,有属性:品牌、型号、排量、速度,有方法:启动、加速、转弯、刹车、息火
    jenkins 集成 pytest + allure
    jenkins环境安装(windows)
  • 原文地址:https://www.cnblogs.com/lizhaowen/p/6700165.html
Copyright © 2011-2022 走看看