生活如同楼梯般的上上下下

生活如同楼梯般的上上下下

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

首先解释下,我每次贴出来的代码。都是只给出主要部分的代码。便于大家理解,有人说运行不了的。请谅解哈。

上次我们简单的介绍了简单工厂模式,我们知道,只要有统一归类的操作,我们就可以抽象出一个父类出来,然后根据不同的条件来用工厂模式实例化不同的对象,然后用父类的姿态显示出来。但是相对于我们面向对象的编程思想来说。这种简单工厂模式还是有一定的缺点的,一旦我们需要再添加新的操作,就需要再定义一个新的子类并且继承同一个父类,而且需要修改工厂类中的代码,因为我们要添加新的子类和判断条件(比如我需要给计算器增加一个开平方的功能),在Operation类还需要增加相应的判断条件。很明显,这种模式的有着高耦合,一旦我们需要改变需求,就需要大量修改修改原本已经写好的代码,在需求的不断变化过程中,这不是一件很好的事。

现在呢。在我们面前会再有一个案例。一个商场打算做一种促销,促销方式有两种。
1.购物满500返200.
2.超过200元打8折。
然后计算出客户购买商品之后应付的金额。
这样的需求在不变得情况下是很容易写出来的。但是,如果我需要把打折的数字和返现的数字是需要变化的呢?或者我们需要再添加一种新的打折方式呢?
虽然简单工厂模式可以实现这个功能,但是商场的这种促销模式是不断的改变的,所以简单工厂的代码就需要不断的修改,程序就需要不断的再次编译和生产。那么好的方法是什么呢?
面对算法的经常变动,策略模式就应运而生。

面向对象的编程,并不是类越多就越好,类的划分是为了封装,但是分类的基础和原则是抽象,具有相同属性和功能的对象的抽象集合才是类。
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,该模式让算法的变化,不会影响到使用算法的客户。
首先。定义算法抽象类,算法抽象类中有个算法实现的抽象方法,各种算法继承算法抽象类,然后重写算法抽象方法
其次,定义Context类。Context为上下文类,在该类中先声明一个抽象类的对象,然后按照客户所选的条件实例化具体的算法对象。然后再在该类中写一个方法,这个方法中其实是调用了抽象类的方法,用来显示算法对象的功能。
最后我们只需要定义不同的算法来继承算法抽象类即可。
下面给大家看看策略模式的图解:




关于上面我们解释的策略模式,我们就用收银这个案例来实现下。代码虽然是我敲到博客中来的,但是该案例出自大话设计模式中。

namespace 收银台
{
    /// <summary>
    /// 现金收费抽象类
    /// </summary>
   abstract class CashSuper
    {
       public abstract double acceptCash(double money);
    }
    /// <summary>
    /// 正常收费继承CashSuper
    /// </summary>
   class CashNormal : CashSuper
   {
       public override double acceptCash(double money)
       {
           return money;
       }
   }
    /// <summary>
    /// 打折收费类
    /// </summary>
   class CashRebate : CashSuper
   {
       private double moneyRebate = 1d;
       public CashRebate(string moneyRebate)
       {
           //在构造方法中对打折进行赋值
           this.moneyRebate = double.Parse(moneyRebate);
       }

       public override double acceptCash(double money)
       {
           return money*moneyRebate;
       }
   }
    /// <summary>
    /// 现金返利类
    /// </summary>
   class CashReturn : CashSuper
   {
       private double moneyCondition = 0.0d;//返利起点
       private double moneyReturn = 0.0d;//返利金额
       public CashReturn(string moneyCondition, string moneyReturn)
       {
           //对返利起点,和返利金额初始化
           this.moneyCondition = double.Parse(moneyCondition);
           this.moneyReturn = double.Parse(moneyReturn);
       }
       public override double acceptCash(double money)
       {
           double result=money;
           if (money > moneyCondition)
               result = money - Math.Floor(money / moneyCondition) * moneyReturn;//超过返利起点的部分全部都参加返利?
           return result;
       }
   }
    /// <summary>
    /// 现金收费工厂类
    /// 有了策略模式 就不在需要这个简单工厂来产生需要的对象。
    /// </summary>
   class CashFactory
   {
       public static CashSuper CreatCashAccept(string type)
       {
           CashSuper cs = null;
           switch (type)
           {
               case "正常收费": cs=new CashNormal(); break;
               case "满300反100": cs = new CashReturn("300","100"); break;
               case "打8折": cs = new CashRebate("0.8"); break;
           }
           return cs;
       }
   }
    /// <summary>
    /// 上下文类
    /// </summary>
    class Context
    {
        private CashSuper cs=null;
        public Context(string type)
        {
            //修改了Context 把实例化子类 直接放在了上下文的构造方法中
            //参数是有UI界面得来的 彻底分离了UI与逻辑。
            switch (type)
            {
                case "正常收费": cs = new CashNormal(); break;
                case "满300反100": cs = new CashReturn("300", "100"); break;
                case "打8折": cs = new CashRebate("0.8"); break;
            }
        }
        public double Getresult(double money)
        {
            return cs.acceptCash(money);
        }
    }
}

 然后,我们再给出客户端的主要代码。

private void btnok_Click(object sender, EventArgs e)
        {
            /*在这里使用策略模式
             一样是创建上下文类来控制到底使用哪种算法
             需要用上下文类来实现算法的选择以及把实例化的子类赋值给
             * 上下文中的父类。
             * 再用上下文类中的方法来调用父类的从子类中获得到的方法。
             */
            Context ct = new Context(cmb1.SelectedItem.ToString());
            double totalPrice = 0d;
            totalPrice = ct.Getresult(Convert.ToDouble(txtboxprice.Text) * Convert.ToDouble(txtboxnumber.Text));//计算每个商品的总价
            total = total + totalPrice;
            listBox1.Items.Add("单价:" + txtboxprice.Text + "数量" + txtboxnumber.Text + " " + cmb1.SelectedText + "合计" + totalPrice.ToString());
            labCount.Text = total.ToString();
        }

 我们从客户端的代码中不难发现,我们把进行判断的事情都放在了上下文Context类中。这个类不但维护了判断条件,而且所有算法对象的实现都是在这个类中产生。一旦需要修改,我们需要做的就是添加算法类继承抽象算法,和在Context类中增加对该算法的判断即可。
虽然这个策略模式在需求变化的同时,并没有完全的解耦,还是需要修改程序代码,但是相比简单工厂的需要修改两处,这里需要修改一处。是不是已经好了很多呢?

我们拿出来计算器-简单工厂的客户端代码。

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入第一个数:");
            int num1 = int.Parse(Console.ReadLine());
            Console.WriteLine("请输入第二个数:");
            int num2 = int.Parse(Console.ReadLine());
            Console.WriteLine("请输入运算符:");
            string mark = Console.ReadLine();
            Operation opc = FactoryClass.Panduan(mark);
            if (opc != null)
            {
                Console.WriteLine(opc.Run(num1, num2));
            }
            else
            {
                Console.WriteLine("无效的运算符!");
 
            }
            Console.ReadKey();
        }
    }

 在这里,我们的客户端需要认识两个类才能完成计算的操作,Operation,和Factory。
但是我们回头看看策略模式,我们的客户端只需要认识一个Context上下文类,就能完全实现这些功能,耦合度也就随之降低了。
策略模式和简单工厂的配合使用
策略模式是一种定义一系列算法的方法,从概念上看,所有的这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各类算法与算法类之间的耦合性。策略模式也简化了单元测试,每个算法都有自己的类,可以通过直接的接口进行单独的测试。策略模式就是用来封装算法的,但是在实践中,几乎所有的类型规则,只要在分析过程中,听到在 不同的时间应用不同的业务规则。就可以考虑使用策略模式处理这种变化的可能性。

 

 

posted on 2014-03-20 19:56  生活如同楼梯般的上上  阅读(733)  评论(0编辑  收藏  举报