次日,小菜再来找大鸟,问道:“你昨天说计算器这样的小程序还可以用到面向对象三大特性?继承和多态怎么可能用得上,我实在不可理解。”

       大鸟:“小菜很有钻研精神吗?好,今天我让你功力加深一级。你先要考虑一下,你昨天写的这个代码,能否做到很灵活的可修改和扩展呢?”

       小菜:“我已经把业务和界面分离了呀,这不是很灵活了吗?”

       大鸟:“那我问你,现在如果我希望增加一个开根(sqrt)运算,你如何改?”

       小菜:“那只需要改Operation类就行了,在switch中加一个分支就行了。”

       大鸟:“问题是你要加一个平方根运算,却需要把加减乘除的运算都得来参与编译,如果你一不小心,把加法运算改成了减法,这不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员的(时薪)算法,但按照你昨天的程序写法,公司就必须要把包含有的原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘TMD,公司给我的工资这么低,我真是郁闷,这会有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句:

C#代码
  1. if (员工是小菜)  
  2. {  
  3.     salary = salary * 1.1;  
  4. }  

 

       那就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。你明白了吗?”

       小菜:“哦,你的意思是,我应该把加减乘除等运算分离,修改其中一个不影响另外的几个,增加运算算法也不影响其它代码,是这样吗?”

       大鸟:“自己想去吧,如何用继承和多态,你应该有感觉了。”

       小菜:“OK,我马上去写。”

C#代码
  1.    /// <summary>  
  2.    /// 运算类  
  3.    /// </summary>  
  4.    class Operation  
  5.    {  
  6.        private double _numberA = 0;  
  7.        private double _numberB = 0;  
  8.          
  9.        /// <summary>  
  10.        /// 数字A  
  11.        /// </summary>  
  12.        public double NumberA  
  13.        {  
  14.            getreturn _numberA; }  
  15.            set{ _numberA = value;}  
  16.        }  
  17.   
  18.        /// <summary>  
  19.        /// 数字B  
  20.        /// </summary>  
  21.        public double NumberB  
  22.        {  
  23.            getreturn _numberB; }  
  24.            set{ _numberB = value; }  
  25.        }  
  26.   
  27.        /// <summary>  
  28.        /// 得到运算结果  
  29.        /// </summary>  
  30.        /// <returns></returns>  
  31.        public virtual double GetResult()  
  32.        {  
  33.            double result = 0;   
  34.            return result;  
  35.        }  
  36.   
  37.         
  38.    }  

 

C#代码
  1.     /// <summary>  
  2.     /// 加法类  
  3.     /// </summary>  
  4.     class OperationAdd : Operation  
  5.     {  
  6.         public override double GetResult()  
  7.         {  
  8.             double result = 0;   
  9.             result = NumberA + NumberB;  
  10.             return result;  
  11.         }  
  12.     }  
  13.   
  14.     /// <summary>  
  15.     /// 减法类  
  16.     /// </summary>  
  17.     class OperationSub : Operation  
  18.     {  
  19.        public override double GetResult()  
  20.         {  
  21.             double result = 0;  
  22.             result = NumberA - NumberB;  
  23.             return result;  
  24.         }  
  25.     }  
  26.   
  27.     /// <summary>  
  28.     /// 乘法类  
  29.     /// </summary>  
  30.     class OperationMul : Operation  
  31.     {  
  32.         public override double GetResult()  
  33.         {  
  34.             double result = 0;  
  35.             result = NumberA * NumberB;  
  36.             return result;  
  37.         }  
  38.     }  
  39.   
  40.     /// <summary>  
  41.     /// 除法类  
  42.     /// </summary>  
  43.     class OperationDiv : Operation  
  44.     {  
  45.         public override double GetResult()  
  46.         {  
  47.             double result = 0;  
  48.             if (NumberB==0)  
  49.                 throw new Exception("除数不能为0。");  
  50.             result = NumberA / NumberB;  
  51.             return result;  
  52.         }  
  53.     }  

        小菜:“大鸟哥,我按照你说的方法写出来了一部分,首先是一个运算类,它有两个Number属性,主要用于计算器的前后数,然后有一个虚方法GetResult(),用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了GetResult()方法,这样如果要修改任何一个算法,都不需要提供其它算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”

       大鸟:“写得很不错吗,大大超出我的想象了,你现在的问题其实就是如何去实例化对象的问题,哈,今天心情不错,再教你一招‘简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象(比如增加开根运算),这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂,来,我们看看这个类如何写。”

C#代码
  1. /// <summary>  
  2.  /// 运算类工厂  
  3.  /// </summary>  
  4.  class OperationFactory  
  5.  {  
  6.      public static Operation createOperate(string operate)  
  7.      {  
  8.          Operation oper = null;  
  9.          switch (operate)  
  10.          {  
  11.              case "+":  
  12.                  {  
  13.                      oper = new OperationAdd();  
  14.                      break;  
  15.                  }  
  16.              case "-":  
  17.                  {  
  18.                      oper = new OperationSub();  
  19.                      break;  
  20.                  }  
  21.              case "*":  
  22.                  {  
  23.                      oper = new OperationMul();  
  24.                      break;  
  25.                  }  
  26.              case "/":  
  27.                  {  
  28.                      oper = new OperationDiv();  
  29.                      break;  
  30.                  }  
  31.           }  
  32.   
  33.          return oper;  
  34.      }  
  35.  }  

       大鸟:“哈,看到吧,这样子,你只需要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果。”

C#代码
  1. Operation oper;  
  2. oper = OperationFactory.createOperate("+");  
  3. oper.NumberA = 1;  
  4. oper.NumberB = 2;  
  5. double result = oper.GetResult();  

       大鸟: “哈,界面的实现就是这样的代码,不管你是控制台程序,Windows程序,Web程序,PDA或手机程序,都可以用这段代码来实现计算器的功能,当有一天我们需要更改加法运算,我们只需要改哪里?”

       小菜:“改OperationAdd 就可以了。”

       大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”

       小菜:“只要增加相应的运算子类就可以了呀。”

       大鸟: “嗯?够了吗?”

       小菜:“对了,还需要去修改运算类工厂,在switch中增加分支。”

       大鸟: “哈,那才对,那如果要修改界面呢?”

       小菜:“那就去改界面呀,关运算什么事呀。”

       小菜:“ 回想那天我面试题写的代码,我终于明白我为什么写得不成功了,原来一个小小的计算器也可以写出这么精彩的代码,谢谢大鸟。”

       (下为当时面试题时小菜所写代码,见《小菜和大鸟的编程故事之二》)

C#代码
  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         Console.Write("请输入数字A:");  
  6.         string A = Console.ReadLine();  
  7.         Console.Write("请选择运算符号(+、-、*、/):");  
  8.         string B = Console.ReadLine();  
  9.         Console.Write("请输入数字B:");  
  10.         string C = Console.ReadLine();  
  11.         string D = "";  
  12.   
  13.         if (B == "+")  
  14.             D = Convert.ToString(Convert.ToDouble(A) + Convert.ToDouble(C));  
  15.         if (B == "-")  
  16.             D = Convert.ToString(Convert.ToDouble(A) - Convert.ToDouble(C));  
  17.         if (B == "*")  
  18.             D = Convert.ToString(Convert.ToDouble(A) * Convert.ToDouble(C));  
  19.         if (O == "/")  
  20.             D = Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C));  
  21.   
  22.         Console.WriteLine("结果是:" + D);  
  23.     }       
  24. }  

       大鸟: “吼吼,记住哦,编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简炼,更加容易维护,容易扩展和复用,只有这样才可以是真的提高。写出优雅的代码真的是一种很爽的事情。不过学无止境,其实这才是理解面向对象的开始呢。给你出个作业,做一个商场收银软件,营业员根据客户购买最新送白菜网单价和数量,向客户收费。”

       小菜:“就这个?没问题呀。”

除非特别注明,鸡啄米文章均为原创
转载请标明本文地址:http://www.teaching4real.com/software/330.html
2013年5月28日
作者:鸡啄米 分类:软件开发 浏览: 注册送白菜网:6