【DM】设计模式再回顾—策略模式


  • 写在前面的
  • 策略模式
    • 概念
    • 具体实例
    • 比较和理解
  • 总结

写在前面的

  • 上一篇文章我们说到,如果我们需要在原本已经整理好的代码中添加新的内容(包括算法或者功能性模块),我们可以应用简单工厂来实现,比如添加算法(活学活用嘛)、在已有的功能模块中再添加新的功能等。但是,我们在上一篇文章中提到过开放-封闭原则,试想一下,如果我们写出来的代码在应用过程中一直需要不断的调整和增加新的功能,那么每次维护都要打开这个工厂,添加相应的功能或算法之后再重新部署,这样无意间会增加我们的工作量,所以简单工厂在某种意义上就不适用了。

  • 了解到了简单工厂模式的局限性,那么我们就要学习新的设计模式了,也就是我接下来要讲到的“策略模式”

策略模式

概念

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

  • 从定义可以看出,我们所要往原来的代码中添加的某种功能或者算法在一定的情景下是可以“替换”的,具体的实例我们后面会讲到。

  • 来看一下策略模式的UML图:

【DM】设计模式再回顾---策略模式

  • 需要注意的是,Context类与与Strategy之间是聚合的关系

具体实例

  • 原来打算是将上一篇中的计算器用策略模式来实现,但是感觉这样的话不是能够很好的体现封装的思想,所以还是决定按照书上的例子再加上我的总结来说明策略模式。

  • 首先,我们来创建上述的几个类,UML图如下:

【DM】设计模式再回顾---策略模式

 //超类,需要被具体算法类继承来重写
abstract class CashSuper
{
    public abstract double acceptCash(double money)
    {
        return money;
    }
}

//正常收费子类
class CashNormal : CashSuper
{
    public override double acceptCash(double money)
    {
        return money;
    }
}

//打折收费子类
class CashRebate : CashSuper 
{
    private double moneyRebate = 1d; //初始化打折率
    public CashRebate(string moneyRebate)
    {
        //从CLIENT获取打折率
        this.moneyRebate=double.Parse(moneyRebate);
    }

    public override double acceptCash(double money)
    {
        //将CLIENT传来的参数进行计算
        return money * moenyRebate;
    }
}


class CashReturn : CashSuper
{
    //初始化参数
    private double moenyCondition = 0.0d;
    private double moneyReturn= 0.0d;

    //从CLIENT获取参数值
    public CashReturn(string mC,string mR)
    {
        this.moneyCondition=double.Parse(mC);
        this.moneyReturn=double.Parse(mR);
    }

    //其实这里无论计算的方法是什么样子的都无所谓,我们只需要理解这样一个思路
    public override couble acceptCash(double money)
    {
        double result =money;
        if (money >= moneyCondition)
            result = money - Math.Floor(money/moneyCondition) * moneyReturn; //Math.Floor()表示取出该值最接近的整数

        return result;
    }
}
  • 然后,就是最关键的地方了,我们需要在CashContext中体现商场的现阶段的所有促销活动,创建CashContext类:
//在这类加入简单工厂模式,方便引用
class CashContext
{
    //由于我们需要调用CashSuper下的所有的方法,并且需要与CashSuper有“聚合”的关系,所以我们需要初始化CashSuper
    //对于一些需要存值的对象实例化时一定要初始化,否则会吃很大的亏,最要命的一点就是“空值”和NULL
    CashSuper cs = null;

    public CashContext(string type)
    {
        switch (type)
        {
            case"正常收费":
                cashNormal cn =new CashNormal();
                cs=cs0;
                break
            case"满300返100":
                CashReturn cr1 =new CashReturn("300","100");
                cs=cr1;
                break;
            case"打八折":
                CashRebate cr2=new CashRebate("0.8");   
                cs=cr2;
                break;  
        }
    }

    public double GetResult(double money)
    {
        return cs.acceptCash(moeny);
    }
}
  • 最后,我们来写一下CLIENT代码,由于实现的方法有很多,所以我之将重要的一部分写出来,其他的部分由文字代替。
//初始化参数
double total=0.0d;
private void Main()
{
    //获取促销活动类型
    CashContext csuper=new CashContext("这里传入需要进行的促销活动,如打八折")

    //获取顾客消费金额
    double totalPrices=0d;
    totalPrices=cspuer.GetResult("传入本次消费的金额");

    //计算并显示消费金额
    total=total+totalPrices;
    Console.WriteLine("本次消费金额是:[0]",total);
}

比较和理解

  • 首先,我们比较一下简单工厂模式策略模式+简单工厂在代码量上的区别。在简单工厂中,如果我们要在客户端实例化计算类,就需要实现工厂类以及算法类,但是在策略模式+简单工厂中,我们只是实现了一个CashContext类,所以我们在代码量上有了非常明显的减少。

  • 其次,我们来分析一下这两个设计模式在处理具体问题上有什么不同。我们的所写的代码并不是写完了之后就不需要维护了,事实上我们需要一直对代码进行功能或者算法的增加或者修改等操作。试想一下,如果我们的超市一直推出新的促销活动,如果在这样的情况下我们使用简单工厂(并不是不能用),那么在每一次的维护中,我们都要打开工厂类去添加新的CASE,然后再添加一个与之对应的算法。如果在某一次的维护中,有人不小心把之前的代码做了改动,那么损失就不只是一块两块了,同时这样也违背了我们的开放-封闭原则。但是对于策略模式而言,由于我们之前所写的CashContext类已经把原有的算法做了封装,所以我们再增加新的功能或者算法的时候只需要再新增添一个CashContextNew类,然后在这个新的CashContextNew类中增加CASE,再写出与之对应的算法类就可以了,然后还可以无限制的增加增加下去。为什么可以这样?就是因为我们的CashContext类与CashSuper之间是聚合关系,所以我们可以继续无限制增加新的“促销活动”而不用修改原来的

  • 通过上面的对比总结,我们可以看出,通过一个聚合关系,我们可以在这里问题中完美的实现开放-封闭原则,同时也为我们的代码实现了“易维护”的特性。

总结

  • 一路看过来,通过策略模式的一个聚合关系,就完美的实现了“高内聚,低耦合”的代码设计理念,打到了代码的易扩展与易维护,可见设计模式的力量还是很大的。

  • 其实,在一开始的时候我并不倾向于把所有的代码都总结并写在这里,但是为了自己再练习回顾一遍,这个懒还是不偷了,因为设计模式不能只停留在自己的脑海中,一定要自己亲自实现一遍这样的印象才会更加深刻。

TO BE CONTINUE……