zoukankan      html  css  js  c++  java
  • 【简单工厂模式】的美妙体会 -06

    (续上篇) 
         次日,小菜再来找大鸟,问道:“你昨天说计算器这样的小程序还可以用到面向对象三大特性?继承和多态怎么可能用得上,我实在不可理解。”
         大鸟:“小菜很有钻研精神嘛!好,今天我让你功力加深一级。你先要考虑一下,你昨天写的这个代码,能否做到很灵活的可修改和扩展呢?”
         小菜:“我已经把业务和界面分离了呀,这不是很灵活了吗?”
         大鸟:“那我问你,现在如果我希望增加一个开根(sqrt)运算,你如何改?”
         小菜:“那只需要改Operation类就行了,在switch中加一个分支就行了。”
         大鸟:“问题是你要加一个平方根运算,却需要把加减乘除的运算都得来参与编译,如果你一不小心,把加法运算改成了减法,这不是大大的糟糕。

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

    if (员工是小菜)
    {
        salary = salary * 1.1;
    }

    那就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。你明白了吗?”
          小菜:“哦,你的意思是,我应该把加减乘除等运算分离,修改其中一个不影响另外的几个,增加运算算法也不影响其它代码,是这样吗?”
          大鸟:“自己想去吧,如何用继承和多态,你应该有感觉了。”
          小菜:“OK,我马上去写。”

    //运算类
    public class Calculator
    {
        private double _numberA=0;
        private string _operate="";
        private double _numberB=0;
    
        //数字A
        public double NumberA
        {
            get{return _numberA;}
            set {_numberA = value;}
        }
    
        //数字B
        public double NumberB
        {
            get{return _numberB;}
            set {_numberB = value;}
        }
    
        //得到运算结果
        public virtual double GetResult()
        {
            double result =0;
            return result;
        }
    }
    
    //加法
    public CalculatorAdd : Calculator
    {
        public override double GetResult()
        {
            double result=0;
            result = NumberA + NumberB;
            return result;
        }
    }
    
    //减法
    public CalculatorSub : Calculator
    {
        public override double GetResult()
        {
            double result=0;
            result = NumberA - NumberB;
            return result;
        }
    }
    
    //乘法
    public CalculatorRide : Calculator
    {
        public override double GetResult()
        {
            double result=0;
            result = NumberA * NumberB;
            return result;
        }
    }
    
    //除法
    public CalculatorDiv : Calculator
    {
        public override double GetResult()
        {
            double result=0;
            if(NumberB ==0)
                throw new Exception("除数不能为0。");
            result = NumberA / NumberB;
            return result;
        }
    }
    运算类—运算类的子类

          小菜:“大鸟哥,我按照你说的方法写出来了一部分,首先是一个运算类,它有两个Number属性,主要用于计算器的前后数,然后有一个虚方法GetResult(),用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了GetResult()方法,这样如果要修改任何一个算法,都不需要提供其它算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”
          大鸟:“写得很不错吗,大大超出我的想象了,你现在的问题其实就是如何去实例化对象的问题,哈,今天心情不错,再教你一招‘简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象(比如增加开根运算),这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程这就是工厂,来,我们看看这个类如何写。”

    //运算类工厂
    public class OperationFactory
    {
        public string createOperate(string operate)
        {
            Calculator calc = null;
            switch(operate)
            {
                case "+":
                    calc = new CalculatorAdd();
                    break;
                case "-":
                    calc = new CalculatorSub();
                    break;                
                case "*":
                    calc = new CalculatorRide();
                    break;                
                case "/":
                    calc= new CalculatorDiv();
                    break;                        
            }
            return calc;
        }
    }
    运算类工厂

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

    //中间类
    public class Operation
    {
        public static double GetResult01(double numberA, double numberB, string operate)
        {
            double result = 0d;
    
            Calculator calc = null;
    
            calc = OperationFactory.createOperate(operate);
            calc.NumberA = numberA;
            cal.NumberB = numberB;
            result = calc.GetResult();
            return result;
        }
    }
    中间类

          大鸟: “哈,界面的实现就是这样的代码,不管你是控制台程序,Windows程序,Web程序,PDA或手机程序,都可以用这段代码来实现计算器的功能,当有一天我们需要更改加法运算,我们只需要改哪里?”
          小菜:“改OperationAdd 就可以了。”
          大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”
          小菜:“只要增加相应的运算子类就可以了呀。”
          大鸟: “嗯?够了吗?”
          小菜:“对了,还需要去修改运算类工厂,在switch中增加分支。”
          大鸟: “哈,那才对,那如果要修改界面呢?”
          小菜:“那就去改界面呀,关运算什么事呀。”

    class InheritPoly
    {
        static void Main(string[] args)
        {
            try
            {
                Console.Write("请输入数字A:");
                string strNumberA = Console.ReadLine();
                Console.Write("请选择运算符号(+、-、*、/):");
                string strOperate = Console.ReadLine();
                Console.Write("请输入数字B:");
                string strNumberB = Console.ReadLine();
                string strResult ="";
    
                strResult = Convert.ToString(Operation.GetResult01(Convert.ToDouble(strNumberA), Convert.ToDouble(strNumberB), strOperate);
    
                Console.WriteLine("结果是:" + strResult);
                Console.readLine();
    
            }
            catch(Exception ex)
            {
                Console.WriteLine("您这里输入有错:" + ex.Message);
            }
        }
    }
    界面实现

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

    (下为当时面试题时小菜所写代码,见《小菜编程成长记(一)》)

    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("请输入数字A:");
            string A = Console.ReadLine();
            Console.Write("请选择运算符号(+、-、*、/):");
            string B = Console.ReadLine();
            Console.Write("请输入数字B:");
            string C = Console.ReadLine();
            string D = "";
    
            if (B == "+")
                D = Convert.ToString(Convert.ToDouble(A) + Convert.ToDouble(C));
            if (B == "-")
                D = Convert.ToString(Convert.ToDouble(A) - Convert.ToDouble(C));
            if (B == "*")
                D = Convert.ToString(Convert.ToDouble(A) * Convert.ToDouble(C));
            if (O == "/")
                D = Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C));
    
            Console.WriteLine("结果是:" + D);
        }     
    }
    失败的代码

    原文摘自:http://www.cnblogs.com/cj723/archive/2006/09/23/512430.html

  • 相关阅读:
    Openrasp源码分析
    feifeicms后台任意文件读取
    python之迭代器和生成器
    java之导入excel
    jquery单击事件的写法
    java之高效操作文件
    多条件搜索优化sql
    java之代码复用
    java之接口文档规范
    eclipse之常用快捷键
  • 原文地址:https://www.cnblogs.com/fightingtong/p/3580295.html
Copyright © 2011-2022 走看看