简单工厂模式

  手上有一个《大话设计模式》,之前已经看了一些了,但是没有认真做一做记录。这里是第一个最简单的设计模式:简单工厂模式

  工厂当然就是生产东西的,在程序设计中(面向对象的程序设计)这个工厂则是负责生产对象的。

先看一个写一个控制台计算器的例子:要求实现一个控制台计算器,输入两个数和运算符号,然后得出结果。

版本1:

class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("输入A:");
                int numA = Convert.ToInt32(Console.ReadLine());
                Console.WriteLine("选择运算符(+ - * /):");
                string strOperate = Console.ReadLine();
                Console.WriteLine("输入B:");
                int numB = Convert.ToInt32(Console.ReadLine());
                string strResult = "";
                switch (strOperate)
                {
                    case "+":
                        strResult = (numA + numB).ToString();
                        break;
                    case "-":
                        strResult = (numA - numB).ToString();
                        break;
                    case "*":
                        strResult = (numA * numB).ToString();
                        break;
                    case "/":
                        if (numB!=0)
                        {
                            strResult = (numA / numB).ToString();
                        }
                        else
                        {
                            strResult = "除数不能为0!";
                        }
                        break;
                    default:
                        strResult = "非法操作符";
                        break;
                }

                Console.WriteLine("运算结果是:"+strResult);
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("输入有错:"+ex.Message);
            }
        }
    }

就运行来说完全没有问题,但是就这样真的没毛病吗?这是用计算机的思维在写程序,完全没有面向对象的思想,比如要把这个东西改成一个windows的计算器,或者web上,代码就不能用了。

封装:

将业务逻辑封装起来,和界面逻辑分开,把运算的部分封装成一个类。这样就让这个运算类在其它地方复用了。

版本2:

/// <summary>
    /// 运算类
    /// </summary>
    public class Operation
    {
        /// <summary>
        /// 获取计算结果
        /// </summary>
        /// <param name="numA">数字A</param>
        /// <param name="numB">数字B</param>
        /// <param name="strOperate">运算符</param>
        /// <returns></returns>
        public static double GetResult(double numA,double numB,string strOperate)
        {
            double result = 0d;
            switch (strOperate)
            {
                case "+":
                    result = numA + numB;
                    break;
                case "-":
                    result = numA - numB;
                    break;
                case "*":
                    result = numA * numB;
                    break;
                case "/":
                    if (numB != 0)
                    {
                        result = numA / numB;
                    }
                    else
                    {
                        throw new Exception("除数不能为0");
                    }
                    break;
                default:
                    //strResult = "非法操作符";
                    throw new Exception("不支持的操作符");
            }
            return result;
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("输入A:");
                double numA = Convert.ToDouble(Console.ReadLine());
                Console.WriteLine("选择运算符(+ - * /):");
                string strOperate = Console.ReadLine();
                Console.WriteLine("输入B:");
                double numB = Convert.ToDouble(Console.ReadLine());
                double result = Operation.GetResult(numA,numB,strOperate);
                Console.WriteLine("运算结果是:"+ result);
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("输入有错:"+ex.Message);
            }
        }
    }

面向对象的三大思想之一封装就在这里体现了。还有两大思想呢?

再回头来看看这个计算器,逻辑和界面已经分离了,代码可以在其它地方复用了,还有什么问题呢?如果要增加一个运算呢,比如增加一个开根号运算sqrt需要怎么修改呢?

最容易想到的是就是增加switch的分支吧,再增加一个分支支持开根号就行了这又存在一个问题了:明明只要增加一个开根号运算,但是其它运算规则都来参加编译。书上举了一个特别有意思的例子:如果一个公司系统让做薪资管理系统的维护,原来是 技术人员(月薪),销售(底薪+提成),经理(年薪+股份)三种计算薪水规则,现在要增加兼职人员(时薪)的算法,然后做维护,按照之前的写法,把运算的类拿出来维护,做修改,维护人员除了完成兼职算法以外再悄悄的在技术人员(月薪)地方加一行代码

if(我)
{
    salary = salary*1.1;
}

工资就比原来多了10%(违法要坐牢),所以这样是有风险的!所以这时候应该把各种运算进行分离,在分离的过程中就运用到了继承和多态。

版本3:

/// <summary>
    /// 运算基类
    /// </summary>
    public class Operation
    {
        public double numA { get; set; }
        public double numB { get; set; }
        public virtual double GetResult()
        {
            double result = 0;
            return result;
        }
    }
    /// <summary>
    /// 加法类
    /// </summary>
    class OperateAdd : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = numA + numB;
            return result;
        }
    }
    /// <summary>
    /// 减法类
    /// </summary>
    class OperateSub : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = numA - numB;
            return result;
        }
    }
    /// <summary>
    /// 乘法类
    /// </summary>
    class OperateMul : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = numA * numB;
            return result;
        }
    }
    /// <summary>
    /// 除法类
    /// </summary>
    class OperateDiv : Operation
    {
        public override double GetResult()
        {
            double result = 0;
            result = numA / numB;
            return result;
        }
    }

现在是各种类已经有了,但是要怎么样才能得到不同的运算类呢?这里就需要一个工厂了,根据输入运算符得到对应的运算类。

/// <summary>
    /// 运算类工厂
    /// </summary>
    public class OperationFactory
    {
        public static Operation CreateOperation(string operate)
        {
            Operation oper = null;
            switch (operate)
            {
                case "+":
                    oper = new OperateAdd();
                    break;
                case "-":
                    oper = new OperateSub();
                    break;
                case "*":
                    oper = new OperateMul();
                    break;
                case "/":
                    oper = new OperateDiv();
                    break;
                default:
                    //strResult = "非法操作符";
                    throw new Exception("不支持的操作符");
            }
            return oper;
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("输入A:");
                double numA = Convert.ToDouble(Console.ReadLine());
                Console.WriteLine("选择运算符(+ - * /):");
                string strOperate = Console.ReadLine();
                Console.WriteLine("输入B:");
                double numB = Convert.ToDouble(Console.ReadLine());
                var oper = OperationFactory.CreateOperation(strOperate);
                oper.numA = numA;
                oper.numB = numB;
                double result = oper.GetResult();
                Console.WriteLine("运算结果是:"+ result.ToString());
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("输入有错:"+ex.Message);
            }
        }
    }

这就是简单工厂模式,如果要改界面,那就换界面就行了,如果要增加运算法则,那就增加类就行了,不过还要修改工厂增加switch分支,由工厂来复制创建类,代码复用性和可维护性大大地提高了。

(排版混乱,以后慢慢改进)

posted @ 2017-02-09 11:54  小书丶  阅读(287)  评论(0编辑  收藏  举报