策略模式

模拟场景:

某某公司要求我们做一个商场收银系统,

提出需求:商场会不定时举办一系列的优惠活动,优惠方式暂定为:打折扣,满多少还多少(例如:满300还100)

 

初步场景分析:

看到这个需求,第一感觉就会潜意识的认为“这个太简单了”。

1.商场收银系统:定义为winform的应用程序

2.活动优惠的计算,判断一下就可以了。

 

初步代码实现:

 

[csharp]  view plain copy print ?
  1. /// <summary>  
  2.        /// 点击确定计算收费  
  3.        /// </summary>  
  4.        /// <param name="sender"></param>  
  5.        /// <param name="e"></param>  
  6.        private void btnOk_Click(object sender, EventArgs e)  
  7.        {  
  8.            int number = Convert.ToInt32(this.txtNumber.Text);  
  9.            double price = Convert.ToDouble(this.txtPrice.Text);  
  10.            double total = 0;  
  11.            switch (this.cbxType.SelectedItem.ToString())  
  12.            {  
  13.                case "正常收费":  
  14.                    total = number * price;  
  15.                    break;  
  16.                case "满300返100":  
  17.                    moneyCondition = 300;  
  18.                    moneyReturn = 100;  
  19.                    double money = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text);  
  20.                    if (money >= moneyCondition)  
  21.                    {  
  22.                        total = money - Math.Floor(money / moneyCondition) * moneyReturn;  
  23.                    }  
  24.                    break;  
  25.                case "打8折":  
  26.                    total = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text) * 0.8;  
  27.                    break;  
  28.            }  
  29.            this.lbxList.Items.Add("单价:" + txtPrice.Text + "数量:" + txtNumber.Text + " "  
  30.                + cbxType.SelectedItem.ToString() + "合计: " + total.ToString());  
  31.            this.lblResult.Text = total.ToString();  
  32.   
  33.        }  


好,现在我们来分析一下上面的实现:

咋一看,感觉没什么问题,可再细分析,问题就多了:

1:显示和逻辑紧密的联系在一起。

2:完全过程式的编程,没办法复用。

3:当新需求增加的时候,还需要修改这个条件分支,不符合开放封闭原则,过多的判断不利于维护。

经过以上分析,进行初步的修改:

先来看看结构图:

 

工厂类:

[csharp]  view plain copy print ?
  1. public class CashFactory  
  2.     {  
  3.         public static CashSuper createCashAccpet(string type)  
  4.         {  
  5.             CashSuper cs = null;  
  6.             switch (type)  
  7.             {  
  8.                 case "正常收费":  
  9.                     cs = new CashNormal();  
  10.                     break;  
  11.                 case "满300返100":  
  12.                     cs = new CashReturn("300","100");  
  13.                     break;  
  14.                 case "打8折":  
  15.                     cs = new CashRebate("0.8");  
  16.                     break;  
  17.             }  
  18.             return cs;  
  19.         }  
  20.     }  

运算父类:

[csharp]  view plain copy print ?
  1. /// <summary>  
  2.     /// 现金收取父类,算法的抽象类  
  3.     /// </summary>  
  4.     public abstract class CashSuper  
  5.     {  
  6.         /// <summary>  
  7.         /// 抽象方法:收取现金  
  8.         /// </summary>  
  9.         /// <param name="money">原价</param>  
  10.         /// <returns>当前价</returns>  
  11.         public abstract double acceptCash(double money);  
  12.     }  


折扣类:

[csharp]  view plain copy print ?
  1.  /// <summary>  
  2. /// 打折收费  
  3. /// </summary>  
  4. public class CashRebate:CashSuper  
  5. {  
  6.     private double moneyRebate = 1d;  
  7.   
  8.     public CashRebate(string money)  
  9.     {  
  10.         this.moneyRebate = double.Parse(money);  
  11.     }  
  12.   
  13.     //初始化时必需输入折扣率,如八折就是0.8  
  14.     public override double acceptCash(double money)  
  15.     {  
  16.         return money * moneyRebate;  
  17.     }  
  18. }  


返利类:

[csharp]  view plain copy print ?
  1. /// <summary>  
  2. /// 返利收费  
  3. /// </summary>  
  4. public class CashReturn:CashSuper  
  5. {  
  6.     private double moneyCondition = 0.0d;  
  7.     private double moneyReturn = 0.0d;  
  8.   
  9.     /// <summary>  
  10.     /// 初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100  
  11.     /// </summary>  
  12.     /// <param name="moneyCondition"></param>  
  13.     /// <param name="moneyReturn"></param>  
  14.     public CashReturn(string moneyCondition,string moneyReturn)  
  15.     {  
  16.         this.moneyCondition = double.Parse(moneyCondition);  
  17.         this.moneyReturn = double.Parse(moneyReturn);  
  18.     }  
  19.   
  20.     public override double acceptCash(double money)  
  21.     {  
  22.         double result = money;  
  23.         //若大于返利条件,则需要减去返利值  
  24.         if (money>=moneyCondition)  
  25.         {  
  26.             result = money-Math.Floor(money/moneyCondition)*moneyReturn;  
  27.         }  
  28.         return result;  
  29.     }  
  30. }  


正常收费类:

[csharp]  view plain copy print ?
  1.  /// <summary>  
  2. /// 正常收费  
  3. /// </summary>  
  4. public class CashNormal:CashSuper  
  5. {  
  6.     public override double acceptCash(double money)  
  7.     {  
  8.         return money;  
  9.     }  
  10. }  


好的,经过完善之后我们的代码已经出来了,下面我们再来细细分析一下这代码:

代码使用了简单工厂模式来解决了对象的创建,通过抽象实现了业务逻辑的分离,在维护性上也得到了改善。

那么这代码就没有问题了吗?答案是:有

现在假设我们要为商场添加一种优惠活动,满100积分送10点,以后积分达到一定值就可以领取奖品。

我们该怎么去做呢:现在有了工厂,我们只需要添加一个积分的算法类,

让它继承运算父类(CashSuper),在再工厂条件分支里添加一个满100积分送10点的条件分支,界面再稍稍修改就可以了。

这么想虽然是可以解决问题,但是工厂里包含了所有的收费方式,商场是可能经常性的更改打折额度,添加新的优惠方式,

如果是这样的话,我们每次维护或扩展收费方式都要去改动这个工厂,这样做就不得不让代码重新编译部署,这样的处理方式是很糟糕的。

那么我们该如何去做呢?现在我们就进入正题:其实商场的促销活动,打折活动,返利活动都是一些算法,而算法本身只是一种策略,

最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。
 

 

我们先来看看策略模式的结构图:

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

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

Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用:

客户端的实现:

 

下面我们就用策略模式来改善一下我们的代码:

代码结构图:

现有的代码没有CashContext,我们就添加一个CashContext的类:

其他的类不变,客户端实现:

经过策略模式完善的代码已经实现,下面我们再来分析一下我们的代码:

现在问题又来了,算法的判断又回到客户端里来了,我们该如何去解决这个问题呢?

其实我们可以让策略模式与简单工厂模式相结合,下面我们来看看怎么个实现法:

在CashContext里进行对象的构造:

客户端实现:

 

经过进一步的优化,我们的代码已经比较的优化了,但是一个很明显的问题也显示出来了,就是switch的条件判断分支,

当我们需要添加一种算法的时候,我们还是需要修改CashContext里的switch算法,这样的代码还是让人非常的不爽,哪还有什么方法呢?

我们可以使用反射来改善现有的代码(反射的具体原理与实现将在不久写出):

switch算法的条件判断分支我们抽取出来,用一个xml文件进行记录:

接下来我们只需要在加载事件中将xml的信息读取出来绑定就可以了:

由于我们的switch已经转移到xml中了,那么我们的CashContext类就可以简化成:

我们的具体算法实现类不变,现在我们最主要就是通过反射去实例化不同的算法对象:

 

 总结:

代码经过多次的修改,可维护性,代耦合高内聚的特性已经展现出来了,现在我们再来分析一下上面的代码吧,上面我们通过反射去除了switch分支,

有效的把耦合降到最低,但是这样做也是有一点点代价的,反射会比较耗一点性能,所以我们做程序的时候要具体问题具体分析。

通过上面的代码我们来分析一下策略模式吧:

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

 

应用场景:
1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
 

优点:

1、策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试,每个算法可以保证它没有错误,修改其中任一个时也不会影响其他的算法。

2、策略模式封装了变化,策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,
只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

3、策略模式有利于程序的高内聚、低偶合性,使得程序更加的灵活。

 

缺点:

基本策略模式中所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,
这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就是最大化地减轻了客户端的职责。


以上内容转自(尽供本人学习使用):http://blog.csdn.net/shiyuan17/article/details/9056637 。感谢博主花费时间和经历为我们准备如此好的学习资料。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值