zoukankan      html  css  js  c++  java
  • 代码无错就是优?简单工厂模式 C#

    还是那几句话:

    学无止境,精益求精

    十年河东,十年河西,莫欺少年穷

    学历代表你的过去,能力代表你的现在,学习代表你的将来

    废话不多说,直接进入正题:

    现在给你一道面试题,如下:

    请用C++,C#,Java或VB.NET等任意一种面向对象语言实现一个计算机控制台程序,要求输入任意两个数字和一个运算符号,得到结果。

    你会怎样设计这道程序呢?下面我列举各个面试人员的答卷并作分析(各位看官:看看有没有和你思路一样的答卷):

    面试人员菜鸟一的答卷如下:

        class Program
        {
            static void Main(string[] args)
            {
                Console.Write("请输入数字A:");
                string A = Console.ReadLine();
    
                Console.Write("请输入运算符号B:");
                string B = Console.ReadLine();
    
                Console.Write("请输入数字C:");
                string C = Console.ReadLine();
    
                double D=0;
                if (B == "+")
                {
                    D = Convert.ToDouble(A) + Convert.ToDouble(C);
                }
                if (B == "-")
                {
                    D = Convert.ToDouble(A) - Convert.ToDouble(C);
                }
                if (B == "*")
                {
                    D = Convert.ToDouble(A) * Convert.ToDouble(C);
                }
                if (B == "/")
                {
                    D = Convert.ToDouble(A) / Convert.ToDouble(C);
                }
                Console.WriteLine("结果为" + D);
                Console.ReadKey();
            }
        }
    View Code

    针对菜鸟一的代码,我们作如下分析:

    1、变量/函数命名不规范

    2、判断分支意味着加减乘除四个判断都必须做,使计算机做了三次无用的判断

    3、运算除法时,如果被除数为零时,会异常

    总之:上述方法易读性差,易维护性差,不可重构,不可复用,不可扩展,不灵活。因此菜鸟一未被公司录用。

    .

    面试人员菜鸟二的答卷如下:

            static void Main(string[] args)
            {
                Console.Write("请输入数字A:");
                string strNumberA = Console.ReadLine();
    
                Console.Write("请输入运算符号:");
                string strOperate = Console.ReadLine();
    
                Console.Write("请输入数字B:");
                string strNumberB = Console.ReadLine();
    
                double strResult = 0;
    
                switch (strOperate)
                {
                    case "+": strResult = Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB); break;
                    case "-": strResult = Convert.ToDouble(strNumberA) - Convert.ToDouble(strNumberB); break;
                    case "*": strResult = Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB); break;
                    case "/": if (strNumberB != "0")
                        {
                            strResult = Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB);
                        }
                        else
                        {
                            Console.WriteLine("除数不能为零"); Console.ReadKey(); return;
                        }
                        break;
                }
                Console.WriteLine("结果为" + strResult);
                Console.ReadKey();
            }
    View Code

     针对菜鸟二的答卷,我们作如下分析:

    1、变量/函数命名基本规范

    2、判断分支只运行一次

    3、除法运算判断了被除数不能为零的情况

    虽说基本实现了计算器加减乘数的功能

    但是:

    出题人的意思是利用面向对象语言设计一道计算器程序,菜鸟二的程序看似完美,但是并没有使用面向对象思想,因此:菜鸟二的面试结果亦是未被录取。

    呜呜,那么怎么设计这道程序才能打动面试官呢?

    现在我们深入分析菜鸟二的答卷

    如下:

     1、业务与输出没有分离(针对业务的单独封装,降低耦合度)

    就如同ASP程序和ASP.NET的对比一样。在ASP程序中,页面的HTML代码和业务逻辑CS代码混在一起,看上去十分不舒服,而且容易出错!而,ASP.NET做了输出与业务的分离,也就是Aspx文件和Aspx.cs文件是分开了的!

    OK,根据上述观点分析,我们来构造第三种答卷。

    .

    面试人员菜鸟三的答卷如下:

    计算器操作类:

        public class Operate
        {
            public static double GetResult(double strNumberA, double strNumberB, string strOperate)
            {
                double strResult=0;
                switch (strOperate)
                {
                    case "+": strResult = strNumberA + strNumberB; break;
                    case "-": strResult = strNumberA - strNumberB; break;
                    case "*": strResult = strNumberA * strNumberB; break;
                    case "/": if (strNumberB != 0)
                        {
                            strResult = strNumberA / strNumberB;
                        }
                        else
                        {
                            Console.WriteLine("除数不能为零"); Console.ReadKey();
                        }
                        break;
                }
                return strResult;
            }
        }
    View Code

    页面输出程序:

            static void Main(string[] args)
            {
                Console.Write("请输入数字A:");
                string strNumberA = Console.ReadLine();
    
                Console.Write("请输入运算符号:");
                string strOperate = Console.ReadLine();
    
                Console.Write("请输入数字B:");
                string strNumberB = Console.ReadLine();
    
                double strResult = 0;
    
                Operate.GetResult(Convert.ToDouble(strNumberA), Convert.ToDouble(strNumberB), strOperate);
                Console.WriteLine("结果为" + strResult);
                Console.ReadKey();
            }
    View Code

    针对菜鸟三我们作如下分析:

    1、根据菜鸟三的代码,可以清晰的看出:业务与输出作了分离,也就是说菜鸟三针对业务进行了独立的封装。无论是控制台程序还是Windows应用程序或者是Web应用程序等均可以用这个计算器操作类。

    OK,菜鸟三不仅做到了业务的单独封装,同时也满足了代码的复用性

    但是,菜鸟三依旧做的不算完美,根据出题人的题目要求:运用C++,C#,Java等面向对象语言设计一道计算器程序。注意:这句话的重点是面向对象,我们都知道面向对象设计有三大特性:封装、继承、多态。

    菜鸟三虽然做到了:业务的单独封装代码的复用,但是并没有用到三大特性中的其余两个特性:继承多态

    OK,在这里各位看官可能会问,一个小小的计算器程序,为何非得用继承和多态呢?

    那么,在此,我们针对菜鸟三的答卷作个深入分析,如下:

    1、如果现在要求增加平方根运算、或者增加开根运算,该当如何呢?

    也许各位看官会这样回复我:这个简单,只需要在Switch分支中增加对应的分支就可以了。

    对,各位看官说的不错。

    但是:你增加的这个开根运算需要加减乘除都参与编译,如果在你增加的过程中不小心把加法运算改成了减法运算,那岂不是很糟糕?

    在此:举个例子:

    现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),销售人员(底薪加提成),经理(年薪加股份)三种薪资计算方法。现在需要加入临时工的算法(时薪)。

    如果按照菜鸟三的写法,上述三种计算方法放在一个Switch中即可,增加临时工薪资计算方法也仅仅只需要增加一个Switch分支即可。但是贪心的你除了增加了一个临时工薪资计算分支外,还偷偷增加了一个如下的分支:

    哈哈哈,下个月你就可以领1.1倍薪资了,可能不到下个月你就在看守所了...多么悲哀的一件事情啊!

    那么如何解决或避免上述问题呢?答案:我们将加减乘除这几种运算分别封装即可!

    于是就有了菜鸟四的写法(菜鸟四能想到这么多已经不算菜鸟了~_~)如下:

    运算操作类:

        /// <summary>
        /// 运算基类
        /// </summary>
        public class Operate
        {
            public double strNumberA { get; set; }
            public double strNumberB { get; set; }
    
            public virtual double GetResult()
            {
                double strResult=0;
                return strResult;
            }
        }
    
        /// <summary>
        /// 加法运算
        /// </summary>
        public class OperateAdd : Operate
        {
            public override double GetResult()
            {
                return strNumberA + strNumberB;
            }
        }
    
        /// <summary>
        /// 减法运算
        /// </summary>
        public class OperateSub : Operate
        {
            public override double GetResult()
            {
                return strNumberA - strNumberB;
            }
        }
    
        /// <summary>
        /// 乘法运算
        /// </summary>
        public class OperateMul : Operate
        {
            public override double GetResult()
            {
                return strNumberA * strNumberB;
            }
        }
    
        /// <summary>
        /// 除法运算
        /// </summary>
        public class OperateDiv : Operate
        {
            public override double GetResult()
            {
                if (strNumberB == 0)
                    throw new Exception("被除数不能为零!");
                return strNumberA / strNumberB;
            }
        }
    View Code

    输出代码如下:

            static void Main(string[] args)
            {
                Console.Write("请输入数字A:");
                string strNumberA = Console.ReadLine();
    
                Console.Write("请输入运算符号:");
                string strOperate = Console.ReadLine();
    
                Console.Write("请输入数字B:");
                string strNumberB = Console.ReadLine();
    
                double strResult = 0;
               
                switch (strOperate)
                {
                    case "+": OperateAdd Add = new OperateAdd(); Add.strNumberA = Convert.ToDouble(strNumberA); Add.strNumberB = Convert.ToDouble(strNumberB); strResult = Add.GetResult(); break;
                    case "-": OperateSub Sub = new OperateSub(); Sub.strNumberA = Convert.ToDouble(strNumberA); Sub.strNumberB = Convert.ToDouble(strNumberB); strResult = Sub.GetResult(); break;
                    case "*": OperateMul Mul = new OperateMul(); Mul.strNumberA = Convert.ToDouble(strNumberA); Mul.strNumberB = Convert.ToDouble(strNumberB); strResult = Mul.GetResult(); break;
                    case "/": OperateDiv Div = new OperateDiv(); Div.strNumberA = Convert.ToDouble(strNumberA); Div.strNumberB = Convert.ToDouble(strNumberB); strResult = Div.GetResult(); break;
                }
                Console.WriteLine("结果为:" + strResult);
                Console.ReadKey();
            }
    View Code

    针对菜鸟四的代码分析如下:

    如果需要运算多次,就需要多次重复输出代码的Switch分支,这样做,无疑增加了代码的重复量!虽说菜鸟四的代码非常不错了,但是还是有一定的瑕疵!

    简单工厂模式闪亮登场

    针对菜鸟四代码改进如下(增加工厂类,根据不同的需求,创建不同的对象):

    namespace SJMS
    {
        public class OperateFactory
        {
            /// <summary>
            /// 工厂类,用于创建相应的对象
            /// </summary>
            /// <param name="strOperate"></param>
            /// <returns></returns>
            public static Operate GetOperate(string strOperate)
            {
                Operate Oper = null;
                switch (strOperate)
                {
                    case "+": Oper = new OperateAdd(); break;
                    case "-": Oper = new OperateSub(); break;
                    case "*": Oper = new OperateMul(); break;
                    case "/": Oper = new OperateDiv(); break;
                }
                return Oper;
            }
        }
    
        /// <summary>
        /// 运算基类
        /// </summary>
        public class Operate
        {
            public double strNumberA { get; set; }
            public double strNumberB { get; set; }
    
            public virtual double GetResult()
            {
                double strResult=0;
                return strResult;
            }
        }
    
        /// <summary>
        /// 加法运算
        /// </summary>
        public class OperateAdd : Operate
        {
            public override double GetResult()
            {
                return strNumberA + strNumberB;
            }
        }
    
        /// <summary>
        /// 减法运算
        /// </summary>
        public class OperateSub : Operate
        {
            public override double GetResult()
            {
                return strNumberA - strNumberB;
            }
        }
    
        /// <summary>
        /// 乘法运算
        /// </summary>
        public class OperateMul : Operate
        {
            public override double GetResult()
            {
                return strNumberA * strNumberB;
            }
        }
    
        /// <summary>
        /// 除法运算
        /// </summary>
        public class OperateDiv : Operate
        {
            public override double GetResult()
            {
                if (strNumberB == 0)
                    throw new Exception("被除数不能为零!");
                return strNumberA / strNumberB;
            }
        }
    }
    View Code

    输出代码如下:

            static void Main(string[] args)
            {
                Console.Write("请输入数字A:");
                string strNumberA = Console.ReadLine();
    
                Console.Write("请输入运算符号:");
                string strOperate = Console.ReadLine();
    
                Console.Write("请输入数字B:");
                string strNumberB = Console.ReadLine();
    
                double strResult = 0;
    
                Operate OperateModel = OperateFactory.GetOperate(strOperate);
                OperateModel.strNumberA = Convert.ToDouble(strNumberA);
                OperateModel.strNumberB = Convert.ToDouble(strNumberB);
                strResult = OperateModel.GetResult();
                Console.WriteLine("结果为:" + strResult);
                Console.ReadKey();
            }
    View Code

    OK,上述代码就是由简单工厂模式实现,所谓简单工厂模式我个人的理解是:根据不同的条件,工厂负责生产不同的对象。

    大话设计模式上有一些UML类图,在此不作讲解,直接粘贴图片。

    上述运算类图如下:

    关于UML类图的解读,请各位看官自行查阅资料!

    @陈卧龙的博客

  • 相关阅读:
    flask 返回json数据
    flask 中文乱码
    flask 参数校验
    AOP集成防止连续多次点击问题
    关于横竖屏切换导致的Activity重建问题
    Flutter工程无法找到Android真机或Android模拟器
    Failed to find configured root that contains
    Unable to resolve dependency问题解决
    双重ScrollView,RecyclerView联动实例
    RecyclerView联动滑动失败
  • 原文地址:https://www.cnblogs.com/chenwolong/p/SFactory.html
Copyright © 2011-2022 走看看