设计模式之-简单工厂模式

前言

为了理解和学习简单工厂模式,我们先看一段简单计算器的代码

 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 (B == "/")
                D = Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C));
            Console.WriteLine("结果是:" + D);
            Console.ReadKey();
        }
    }

以上代码存在几点明显问题

①A、B、C、D这样的命名非常不规范,真实项目中应该避免使用

②if判断分支,让计算机多做了三次无用功

③除数的时候如果用户输入了非正数及符号,没有相关处理。

根据上述三点问题进行优化后的代码如下:

 class Program
    {
        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 = "";
                switch (strOperate)
                {
                    case "+":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB));
                        break;
                    case "-":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) - Convert.ToDouble(strNumberB));
                        break;
                    case "*":
                        strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB));
                        break;
                    case "/":
                        if (strNumberB != "0")
                            strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB));
                        else
                            strResult = "除数不能为0";
                        break;
                }
                Console.WriteLine("结果是:" + strResult);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您的输入有错:" + ex.Message);
            }
        }
    }

就上述代码而言,的确实现了简单计算器的功能,但是我们进一步思考这段代码,如果此时 需要新做一个计算器呢?估计有很多小伙伴灵光一现,复制粘贴大法。我们回想下古代活字印刷术的出现,对于印刷的巨大帮助。

⑴ 要改内容,只需要改动要改的文字,此为可维护

⑵这些字并非用完这次就无用了,以后的印刷中可以重复使用,此乃可复用

⑶若此版面内容要加字,只需要另刻字加入即可,这是可扩展

⑷字的排列可能是竖排,可能是横排,此时只需要将活字移动就可做到满足排列需求,此是灵活性好

而再活字印刷术出现之前,以上四点特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本后,此版已无任何可再利用价值。

面向对象的好处

★业务的封装(让业务逻辑与界面逻辑分开,降低耦合度)

根据上述思考,我们将运算单独封装一个运算类 Operation

/// <summary>
    /// 运算类
    /// </summary>
    public class Operaion
    {
        /// <summary>
        /// 计算方法
        /// </summary>
        /// <param name="numberA">运算数</param>
        /// <param name="numberB">运算数</param>
        /// <param name="operate">符号</param>
        /// <returns>返回值</returns>
        public static double GetResult(double numberA, double numberB, string operate)
        {
            double result = 0d;
            switch (operate)
            {
                case "+":
                    result = numberA + numberB;
                    break;
                case "-":
                    result = numberA - numberB;
                    break;
                case "*":
                    result = numberA * numberB;
                    break;
                case "/":
                    result = numberA / numberB;
                    break;
            }
            return result;
        }
    }

客户端调用的时候调用此方法即可:

class Program
    {
        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(Operaion.GetResult(Convert.ToDouble(strNumberA),
                    Convert.ToDouble(strNumberB), strOperate));
                Console.WriteLine("结果是:" + strResult);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您的输入有错:" + ex.Message);
            }
        }
    }

上述代码实现了业务逻辑与界面的分离,但是还没有真正运用到程序的精髓所在:封装、继承、多态,带着思考和疑问,我们做出进一步修改如下:

 /// <summary>
    /// 运算类
    /// </summary>
    public class Operaion
    {
        private double _numberA = 0;
        private double _numberB = 0;
        public double NumberA
        {
            get { return _numberA; }
            set { _numberA = value; }
        }
        public double NumberB
        {
            get { return _numberB; }
            set { _numberB = value; }
        }
        public virtual double GetResult()
        {
            double result = 0;
            return result;
        }
    }
    /// <summary>
    /// 加法类,继承运算类
    /// </summary>
    class OpertionAdd : Operaion
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA + NumberB;
            return result;
        }
    }
    /// <summary>
    /// 减法类,继承运算类
    /// </summary>
    class OperationSub : Operaion
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA - NumberB;
            return result;
        }
    }
    /// <summary>
    /// 乘法类,继承运算类
    /// </summary>
    class OperationMul : Operaion
    {
        public override double GetResult()
        {
            double result = 0;
            result = NumberA * NumberB;
            return result;
        }
    }

    /// <summary>
    ///除法类,继承运算类
    /// </summary>
    class OperationDiv : Operaion
    {
        public override double GetResult()
        {
            double result = 0;
            if (NumberB == 0)
                throw new Exception("除数不能为0.");
            result = NumberA / NumberB;
            return result;
        }
    }

上述代码写完,此时就有一个疑问,如何让计算器知道我们希望用哪一个算法呢?

下面我们看看简单运算工厂类

 public class OperationFactory
    {
        public static Operaion createOperate(string operate)
        {
            Operaion oper = null;
            switch (operate)
            { 
                case "+":
                    oper = new OpertionAdd();
                    break;
                case "-":
                    oper = new OperationSub();
                    break;
                case "*":
                    oper = new OperationMul();
                    break;
                case "/":
                    oper = new OperationDiv();
                    break;
            }
            return oper;
        }

    }

有了运算工厂类,我们只需要输入运算符号,工厂就能实例化适合的对象,通过多态,返回父类的方法实现了计算器的结果,客户端代码如下:

class Program
    {
        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();
                //调用工厂类进行计算
                Operaion oper;
                oper = OperationFactory.createOperate(strOperate);
                oper.NumberA =double.Parse(strNumberA);
                oper.NumberB =double.Parse(strNumberB);
                //返回计算结果
                var  strResult = oper.GetResult();
                Console.WriteLine("结果是:" + strResult);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您的输入有错:" + ex.Message);
            }
        }
    }

这样,不管我们是控制台程序、Windows程序,Web程序或者手机程序,都可以使用这段代码来实现计算器的功能,如果有一天我们需要更改加法运算,我们改OperationAdd就可以了,如果需要增加其他复杂运算,比如平方根、立方根,我们只需要增加相应的运算子类和运算类工厂,再switch中增加分支即可。

UML类图

 

posted @ 2017-07-18 13:31  潇十一郎  阅读(581)  评论(0编辑  收藏  举报