• 设计模式那点事策略模式


    •   应用场景  

      现在我们要做一个商场收银软件,营业员根据所购买商品的单价和数量,向客户收取费用,如果是你,你会怎么设计?

    •   阅读目录

        一:大部分人的写法v1.0

      二:第一次改版后的代码v1.1

      三:第二次改版后的代码v1.2

      四:第三次改版后的代码v1.3

      六:第四次改版后的代码v1.4

      七:对比v1.2版的简单工厂模式和v1.4版的策略模式在客户端的代码对比

      八:策略模式思考

      一:大部分人的写法v1.0

      用两个文本框来输入单价和数量,一个确定按钮来计算出每种商品的费用,用个列表框来记录购买的商品清单,一个标签来记录总价,一个重置按钮来全部清空以便重新开始

      1、Question?

      这样的写法会带来一个问题?什么问题呢?折扣率不灵活性的问题,好的问题来了?什么问题呢?那商场现在搞活动,全场商品一律8折,好,那现在商场又不搞活动了

      2、Answer
      
    全场商品一律8折,那不是就在douTotalPrice后面乘以0.8不就完事了么,商场又不搞活动了,改回来,然后再用改后的程序把客户的机器重新部署一遍

     回答错误

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Strategy
    {
       public partial class Form1 : Form
       {
        public Form1()
        {
          InitializeComponent();
        }
    
          /// <summary>
          /// 确定
          /// </summary>
          double douTotal = 0.0d;//总价
          private void btnOK_Click(object sender, EventArgs e)
          {
             double douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text);//合计
    
          //将每个商品的合计计入总计
              douTotal = douTotal + douTotalPrice;
    
          //列表框中显示购买商品的信息
              this.lvwProduct.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNum.Text + " 合计:" + douTotalPrice.ToString());
              this.lblResult.Text = douTotal.ToString();
           }
        
    /// <summary>     /// 重置     /// </summary>     /// <param name="sender"></param>     /// <param name="e"></param>     private void btnReset_Click(object sender, EventArgs e)     {       this.txtPrice.Text = "";       this.txtNum.Text = "";       this.lvwProduct.Items.Clear();       this.lblResult.Text = "0.00";     }   } }

         

       二:第一次改版后的代码v1.1

      2.1、为什么要改版?

      因为考虑到折扣率是容易变化的,v1.0不具备灵活性,要具备灵活性

      2.2、如何改版?

        做一个下拉列表框,把商场所能考虑的折扣率都写进去,这样需要变化的可能性就会小很多了,就能应付变化的折扣率了

      这次灵活性上比v1.0那版代码好多了,不过重复的代码也挺多的,比如:Convert.ToDouble(),这里就写了5次,而5个分支除了打折多少外不同,几乎没什么区别,应该考虑重构一下

      面向对象编程不是类越多越好,类的划分是为了封装,但是分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类,“打八折”和“打七折”只是形式不同,抽象分析出来,所有的打折算法是一样的,所以打折算法应该是一个类

      2.3、New Question?

      现在商场活动加大了,需要有满300返100的促销算法,又该怎们办?既然满300返100,那么满700就要返200了,万一以后,商场推出其他促销活动,满100积10分,那么满200就要积20分了,积分以后可以换商品,他奶奶的,又过了一阵子,商场又推出了另外一个促销手段,满1000送手机,满2000送电脑,等等类似的无数个促销手段,想想我们到底应该怎么办?怎么才能解决这种可能的无限促销手段的算法?

       2.3.1:打折促销手段
      比如:打5折,打8折

      2.3.2:返利促销手段
      比如:满300返100,那么满700就返200

      2.3.3:送积分促销手段
      比如:满100积10分,那么满200就积20分

      2.3.4:抽奖促销手段
      比如:满1000可以参加抽奖

      2.3.5:送实物促销手段
      比如:满500送天翼手机,满800送微波炉
      2.4、Answer

      有人这样说了,应对这种情况应当首先写一个父类,其次再继承它实现打折的子类和返利的子类以及得积分的子类等等促销手段的子类,利用多态性完成,那么这里很明显就该用到我们上章讲的《迈向架构设计师之路系列—1-简单对象访问模式》一文的思想了,那么应该写几个子类呢?就目前商场的活动来说,“打八折”一个子类,“打七折”一个子类,“打五折”一个子类,“打三折”一个子类,“满300送100”一个子类,“满200送50”一个子类,这都要写6个子类,那么现在商场要“打一折”,“满500送200”,难道还要再去增加子类吗?有必要这样吗?以后只要商场的折扣率和返利一变,有你受的了,你就无限的增加子类吧,想想看这之中哪些是相同的?,哪些是不同的?,对了,打折都是一个行为,只是折扣率不同罢了,“打八折”和“打七折”只是形式不同,抽象分析出来,所有的打折算法是一样的,所以打折算法应该是一个类,返利也只是一个行为,只是返利不同罢了,就打折来说只要有个初始化参数就行了,返利则需要两个初始化参数,满300送100”和“满200送50”只是形式不同,抽象分析出来,所有的返利算法是一样的,所以返利算法应该是一个类

    回答正确

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Strategy
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }  
    
        /// <summary>
        /// 加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
          this.cboType.Items.AddRange(new object[]{"正常收费","打八折","打七折","打五折","打三折"});
          this.cboType.SelectedIndex = 0;
        }
        
    /// <summary>     /// 确定     /// </summary>     double douTotal = 0.0d;//总价     private void btnOK_Click(object sender, EventArgs e)     {       double douTotalPrice = 0d;//合计       switch (this.cboType.SelectedIndex)       {         //正常收费         case 0:           douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text);           break;         //打八折         case 1:           douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.8;           break;         //打七折         case 2:           douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.7;           break;         //打五折         case 3:           douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.5;           break;         //打三折         case 4:           douTotalPrice = Convert.ToDouble(this.txtPrice.Text) * Convert.ToInt32(this.txtNum.Text) * 0.3;           break;       }       douTotal = douTotal + douTotalPrice;       this.lvwProduct.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNum.Text + " 合计:" + douTotalPrice.ToString());       this.lblResult.Text = douTotal.ToString();     }     /// <summary>     /// 重置     /// </summary>     /// <param name="sender"></param>     /// <param name="e"></param>     private void btnReset_Click(object sender, EventArgs e)     {       this.txtPrice.Text = "";       this.txtNum.Text = "";       this.lvwProduct.Items.Clear();       this.lblResult.Text = "0.00";     }   } }

      

      三:第二次改版后的代码v1.2

      3.1、为什么又要改版?

      因为考虑到促销的手段是容易变化的,v1.1不具备灵活性,要具备灵活性

      3.2、UML类图结构

      我们先解析一下v1.2版的UML类图来看,抽象类用斜体表示,继承关系用空心三角形+实现表示,依赖关系用虚线箭头来表示

      

      具体到我们现在的实例上

      

      3.3、New Question?

      对于v1.2版的代码,我们需要增加“打一折”和“满700返300”的促销活动,我们该怎么办?我们只要在收费对象生成工厂中加两个分支,并且在界面的下拉列表框中加两个选项就搞定了,那如果我们增加一种全新的促销手段,“满100返10积分”积分到一定程度可以兑换礼品,我们又该怎么办?加一个积分算法类继承自CashSuper类,积分算法类构造函数接受两个参数,一个是条件一个是返点,再到收费对象生成工厂里面加个,“满100返10积分”的分支就行了,简单工厂模式虽然也能解决这个问题,但是第一个方面:这个模式只是解决对象的创建问题,由于简单工厂本身包含了所有的收费方式,我想每一个商场一定会经常变动打折折扣率和返利额度的以及变更促销手段的,每次维护折扣率和额度以及扩展促销手段都要改动这个收费对象生成工厂,以至于代码要重新编译和部署,这是很糟糕的处理方式,所以用简单对象访问模式不是最好的办法,想想还有其他的方法吗?第二个方面:面对算法的经常变动会应该有更好的解决办法的,是什么呢?大家想想?

      3.4、Answer

       面对算法的经常变动会应该有更好的解决办法的,是什么呢?没错是策略模式(Strategy)

      策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户

      回答正确

    //1:AbstractClass.CashSuper.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace AbstractClass
    {   
         /// <summary>
         /// 现金收取抽象类
        /// </summary>
        public abstract class CashSuper
        {
            /// <summary>
            /// 现金收取抽象类的收取现金抽象方法,参数为原价,返回值为当前价
            /// </summary>
            /// <param name="douMoney">原价</param>
            /// <returns>当前价</returns>
            public abstract double AccpetCash(double douMoney);
        }
    }
    //2:BusinessLogic.CashNormal.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using AbstractClass;
    namespace BusinessLogic
    {
        /// <summary>
        /// 正常收费子类
        /// </summary>
        public class CashNormal:CashSuper
        {
            /// <summary>
            /// 正常收费方法
            /// </summary>
            /// <param name="douMoney">原价</param>
            /// <returns>原价</returns>
            public override double AccpetCash(double douMoney)
            {
                return douMoney;
            }
        }
    }
    //3:BusinessLogic.CashRebate.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using AbstractClass;
    namespace BusinessLogic
    {
        /// <summary>
        /// 打折收费子类
        /// </summary>
        public class CashRebate:CashSuper
        {
            private double douMoneyRebate;//折扣率
            /// <summary>
            /// 构造函数初始化
            /// </summary>
            /// <param name="douMoney">折扣率</param>
            public CashRebate(double douMoneyRebate)
            {
                this.douMoneyRebate = douMoneyRebate;
            }  
            /// <summary>
            /// 打折收费方法
            /// </summary>
            /// <param name="douMoney">原价</param>
            /// <returns>折扣价</returns>
            public override double AccpetCash(double douMoney)
            {
                return douMoney * this.douMoneyRebate;
            }
        }
    }
    //4:BusinessLogic.CashReturn.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using AbstractClass;
    namespace BusinessLogic
    {
        /// <summary>
        /// 返利收费子类
        /// </summary>
        public class CashReturn:CashSuper
        {
            private double douMoneyCondition = 0.0d;//返利条件
            private double douMoneyReturn = 0.0d;//返利值
            /// <summary>
            /// 构造函数初始化
            /// </summary>
            /// <param name="douMoneyCondition">返利条件</param>
            /// <param name="douMoneyReturn">返利值</param>
            public CashReturn(double douMoneyCondition, double douMoneyReturn)
            {
                this.douMoneyCondition = douMoneyCondition;
                this.douMoneyReturn = douMoneyReturn;
            }
    /// <summary> /// 返利收费方法 /// </summary> /// <param name="douMoney">原价</param> /// <returns>返利价</returns> public override double AccpetCash(double douMoney) { double douResult = douMoney; //若大于返利条件,则需要减去返利值 if (douMoney >= douMoneyCondition)   douResult = douMoney - Math.Floor(douMoney / douMoneyCondition) * douMoneyReturn; return douResult; } } } //5:CashFactory.CashFactory.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using AbstractClass; using BusinessLogic; namespace CashFactory { /// <summary> /// 收费对象生成工厂类 /// </summary> public class CashFactory { static CashSuper cashsuper = null; /// <summary> /// 简单工厂 /// </summary> /// <param name="strType">类型</param> /// <returns>现金收取抽象类</returns> public static CashSuper CreateCashAccpet(string strType) { switch (strType) {   case "正常收费":   cashsuper = new CashNormal();   break;   case "打八折":    cashsuper = new CashRebate(0.8);   break;   case "打七折":    cashsuper = new CashRebate(0.7);    break;   case "打五折":    cashsuper = new CashRebate(0.5);    break;   case "打三折":    cashsuper = new CashRebate(0.3);   break;   case "满300返100":    cashsuper = new CashReturn(300,100);   break;   case "满200返50":    cashsuper = new CashReturn(200, 50);    break; } return cashsuper; } } } //6:2-Strategy.Form1.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using AbstractClass; namespace _2_Strategy { public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// <summary> /// 确定 /// </summary> double douTotal = 0.0d;//总价 private void btnOK_Click(object sender, EventArgs e) { double douTotalPrice = 0d;//合计 //利用简单工厂模式根据下拉列表框中选中的项生成相应的对象 CashSuper cashsuper = CashFactory.CashFactory.CreateCashAccpet(this.cboType.SelectedItem.ToString()); douTotalPrice = cashsuper.AccpetCash(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text)); douTotal = douTotal + douTotalPrice; this.lvwProduct.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNum.Text + " 合计:" + douTotalPrice.ToString()); this.lblResult.Text = douTotal.ToString(); } /// <summary> /// 重置 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnReset_Click(object sender, EventArgs e) { this.txtPrice.Text = ""; this.txtNum.Text = ""; this.lvwProduct.Items.Clear(); this.lblResult.Text = "0.00"; } } }

    运行效果图

      四:第三次改版后的代码v1.3
      好了,说到这里,我们看看下面的策略模式UML类图了,也看下策略模式的基本代码结构,我们现在要结合我们当前的这个商城收银软件这个活生生的例子和策略模式来分析一下了,我们目前的v1.2版本的架构设计,CashSuper就是抽象策略,而正常收费CashNormal,打折收费CashRebate,返利收费CashReturn是三个具体的策略,也就是说是策略模式中具体的算法,对比之后发现v1.2版的代码欠缺的就是一个承上启下的Context这个上下文类,同时多了CashFactory这个收费对象生成工厂类,我们现在根据策略模式的UML类图来开始修复我们的v1.2版的代码,在做之前我们还是先解析一下我们的UML类图

      4.1:策略模式结构图

      矩形框分为三层,它代表一个类,类图分为三层,第一层为类名,如果是抽象类,则用斜体表示,第二层为类的特性,通常指的是字段和属性,第三层是类的操作,通常指的是方法和行为,前面如果为“+”表示public,如果为“-”表示private,如果为“#”表示protected,在这幅UMLl类图中共出现了2个关系,一个是继承关系:用空心三角形+实现来表示,还有一种是聚合关系:所谓聚合关系表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但是B对象不是A对象的一部分,举个例子,大雁是群居动物,每只大雁属于一个雁群,一个雁群可以有多只大雁,但是每只大雁又不是雁群的一部分,这里雁群是A对象,每只大雁是B对象,再举个例子,机场包含很多飞机,一个机场可以停放多架飞机,一个机场可以包含多架飞机,但是你不能说每个飞机是机场本身的一部分一样,聚合关系用空心的菱形+实线箭头来表示

      

      具体到我们现在的实例上

      

      4.2:策略模式基本代码

       Strategy类,定义所有的支持的算法的公共接口

    abstract class Strategy
    {
      //算法方法
      public abstract AlgorithmInterface()
    }
    //ConcretStrategy类,封装了具体的算法和行为,继承自Strategy类 //具体算法A class ConcretStrategyA :Strategy { public AlgorithmInterface()   {    Console.WriteKine(“算法A实现");   } } //具体算法B class ConcretStrategyB :Strategy {   public AlgorithmInterface()   {    Console.WriteKine(“算法B实现");   } } //具体算法C class ConcretStrategyC :Strategy {   public AlgorithmInterface()   {    Console.WriteKine(“算法C实现");   } }   //上下文Context,用一个ConcretStrategy具体算法来配置,维护一个对Strategy对象的引用 class Context {   Strategy _strategy;   //初始化时,传入具体的策略对象   public Context(Strategy strategy)   {     this._strategy = strategy;   }   //上下文接口,根据传入的具体策略对象,调用其算法的方法   public void ContextInterface()   {     this._strategy.AlgorithmInterface();   } }   //客户端代码   //由于传入的具体策略对象,所以在调用context.ContextInterface()方法时,得到的是具体策略对象的算法   Context context = new Context (new ConcretStrategyA());   context.ContextInterface();   Context context = new Context (new ConcretStrategyB());   context.ContextInterface();   Context context = new Context (new ConcretStrategyC());   context.ContextInterface();

      4.3:New Question?

       在v1.2版的代码上我们根据策略模式的UML类图,增加一个CashContext类,并修改客户端就行了,但是这样又会带来一个新的问题?什么问题呢?因为去掉了CashFactory这个收费对象生成工厂,所以变成了在客户端去判断用哪一个具体的算法?我们应该怎样才能把这个判断的过程从客户端移走呢?

      4.4:Answer

      在v1.2版的代码里我们是通过简单工厂模式去转移走的,但是现在这里只有一个CashContext类,简单工厂并不是一定是一个单独的类,它为什么不能和策略模式中的CashContext类结合使用呢?

    回答正确

    public Class CashContext
    {
        //声明一个CashSuper对象
      private CashSuper _cashSuper;
    
      /// <summary>
      /// 通过构造函数传入具体的策略
      /// </summary>
      /// <param name="cashsuper">具体的收费策略</param>
      public CashContext(CashSuper cashsuper)
      {
        this._cashSuper = cashsuper;
      }
    
      /// <summary>
      /// 根据具体收费策略的不同,获得不同的计算结果 
      /// </summary>
      /// <param name="douMoney">原价</param>
      /// <returns>当前价</returns>
      public double GetMoney(double douMoney)
      {
        return this._cashSuper.AcceptCash(douMoney);
      }
    }
    
    /// <summary>
    /// 确定
    /// </summary>
    double douTotal = 0.0d;//总价
    private void btnOK_Click(object sender, EventArgs e)
    {
        double douTotalPrice = 0d;//合计
        CashContext cashContext = null;
        switch(this.cbType.SelectedItem.ToString())
        {
            case "正常收费":
                cashContext = new CashContext(new CashNormal());
                break;
            case "打八折":
                cashContext =  new CashContext(new CashRebate(0.8));
                break;
            case "打七折":
                cashContext =  new CashContext(new CashRebate(0.7));
                break;
            case "打五折":
                cashContext =  new CashContext(new CashRebate(0.5));
                break;
            case "打三折":
                cashContext =  new CashContext(new CashRebate(0.3));
                break;
            case "满300返100":
                cashContext =  new CashContext(new CashReturn(300,100));
                break;
            case "满200返50":
                cashContext =  new CashContext(new CashReturn(200, 50));
                break;
        }
        //通过CashContext 的GetMoney()方法的调用,可以得到具体收费策略的计算结果,让具体算法与客户端进行了分离
        douTotalPrice = cashContext.GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
        douTotal = douTotal + douTotalPrice;
        this.lvwProduct.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNum.Text + " 合计:" + douTotalPrice.ToString());
        this.lblResult.Text = douTotal.ToString();
    }

      六:第四次改版后的代码v1.4

      6.1:为什么要改版?

      在客户端去判断用哪一个具体算法,我们应该把这个判断的过程从客户端移走

      策略模式和简单工厂模式结合使用

      修改CashContext类

    public Class CashContext
    {
        //声明一个CashSuper对象
        CashSuper _cashSuper;
        /// <summary>
        /// 构造函数参数不在是一个具体策略对象,而是一个字符串表示收费类型
        /// </summary>
        /// <param name="strTyle">收费类型</param>
        public CashContext(string strTyle)
        {
            case "正常收费":
                _cashSuper = new CashNormal();
                break;
            case "打八折":
                _cashSuper =  new CashRebate(0.8);
                break;
            case "打七折":
                _cashSuper =  new CashRebate(0.7);
                break;
            case "打五折":
                _cashSuper =  new CashRebate(0.5);
                break;
            case "打三折":
                _cashSuper =  new CashRebate(0.3);
                break;
            case "满300返100":
                _cashSuper =  new CashReturn(300,100);
                break;
            case "满200返50":
                _cashSuper =  new CashReturn(200, 50);
                break;
    }
    
        /// <summary>
        /// 根据具体收费策略的不同,获得不同的计算结果 
        /// </summary>
        /// <param name="douMoney">原价</param>
        /// <returns>当前价</returns>
        public double GetMoney(double douMoney)
        {
            this._cashsuper.AcceptCash(douMoney);
        }
    }
    
    /// <summary>
    /// 确定修改客户端代码 Strategy.Form1.cs
    /// </summary>
    double douTotal = 0.0d;//总价
    private void btnOK_Click(object sender, EventArgs e)
    {
        double douTotalPrice = 0d;//合计
        //根据下拉列表框,将相应的字符串传入到CashContext对象中
        CashContext cashContext= new CashContext(this.cbType.SelectedItem.ToString());
        //通过CashContext的GetMoney()方法的调用,可以得到具体收费策略的计算结果,让具体算法与客户端进行了分离
        douTotalPrice = cashContext.GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
        douTotal = douTotal + douTotalPrice;
        this.lvwProduct.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNum.Text + " 合计:" + douTotalPrice.ToString());
        this.lblResult.Text = douTotal.ToString();
    }

      七:对比v1.2版的简单工厂模式和v1.4版的策略模式在客户端的代码对比

      我们发现简单工厂模式,客户端我们需要认识到两个类,一个是:”CashSuper“类,另外一个则是:”CashFactory“类,而策略模式和简单工厂模式的结合,使得客户端只是需要认识一个CashContext 类就可以了,耦合度更加降低了,我们在客户端实例化的是CashContext对象,调用的是CashContext对象的GetMoney()方法,这使得具体的收费策略和客户端分离开来,连父类”CashSuper“类都不让客户端看见

        利用简单工厂模式根据下拉列表框中选中的项生成相应的对象

    CashSuper cashsuper = CashFactory.CashFactory.CreateCashAccpet(this.cboType.SelectedItem.ToString());
    douTotalPrice = cashsuper.AccpetCash(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));
    douTotal = douTotal + douTotalPrice;

      策略模式 

    //根据下拉列表框,将相应的字符串传入到CashContext对象中
    CashContext cashcontext= new CashContext(this.cbType.SelectedItem.ToString());
    //通过CashContext 的GetMoney()方法的调用,可以得到具体收费策略的计算结果,让具体算法与客户端进行了分离
    douTotalPrice = cashcontext .GetMoney(Convert.ToDouble(this.txtPrice.Text) * Convert.ToDouble(this.txtNum.Text));

      八:策略模式思考

      策略模式是一种定义了一系列算法的方法,所有这些算法完成的都是相同的工作(比如:得到对商品要收取的实际价格AcceptCash(double douMoney)),只是实现不同罢了,它可以以相同的方式调用所有的算法(比如:GetMoney()这个相同的方式),减少了各个算法类和使用算法类客户端之间的耦合性(修改各个具体算法类之间不影响,而且客户端看不到具体的算法类)

      策略模式中为Context定义了一系列的可供重用的算法或者行为,继承有助于析取这些算法的公共功能,无论对于打折,返利,返积分,其实都是对商品要收取的实际价格的一种计算方式,通过继承,就可以得到它们的公共功能,公共功能就是指的是Context类中定义的GetMoney()这个方法,这便使得算法间有了抽象的父类CashSuper

      修改其中任何一个算法类,不会影响到其他算法类,可以保证每个算法中不出现错误

      策略模式是用来封装算法的,但是在应用中,可以用来封装任何类型的规则

      在基本的策略模式中,选择具体的策略由客户端承担,并转给策略模式中的Contenxt对象,如v1.3版本中的代码,这没有解决客户端需要选择判断分支的压力,而策略模式和简单工厂模式的结合,选择具体的策略由客户端承担转移到了由Context来承担,这就大大的减轻了客户端的职责,如v1.4版代码,策略模式和简单工厂模式的结合虽然把选择具体的策略由客户端转移到了Context,但是在Context中还是存在switch判断分支语句啊,也就是说我们增加一种算法,比如”打8.8折“,就必须得到switch里面去增加分支判断,商场打折折扣率一变,我们还得去更改分支,这就是我前面提到的一个问题,由于Contenxt本身包含了所有的收费方式,我想每一个商场一定会经常变动打折折扣率和返利额度的,每次维护和扩展收费方式都要改动这个Contenxt类,以至于代码要重新编译和部署,怎么才能连这里面的分支判断都去掉呢?大家想想看,有什么办法?没错这个办法就是反射

  • 相关阅读:
    利用百度云盘API上传文件至百度云盘
    测试Centos硬盘读写速度
    into outfile 生成sql脚本
    Nginx设置Js、Css等静态文件的缓存过期时间
    mysql查询区分大小写
    Table './mysql/proc' is marked as crashed and should be repaired 解决方法
    CentOS 6.6 下配置软RAID5
    管道限流利器pv
    mydumper使用
    RAID详解
  • 原文地址:https://www.cnblogs.com/menglin2010/p/2345941.html
Copyright © 2020-2023  润新知