c# 之简单工厂模式

简单工厂模式

前言:简单工厂模式也是我们编程中经常用到和非常常见的一种设计模式。通过一些书籍和视频的观看后,自己也是写一篇博客来加深自己的理解,已备复习之用。

概念:这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳的方式,我们在创建对象时不会暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决的问题:主要解决接口选择的问题。

何时使用:我们明确的计划不同条件下创建不同的实例的时候。

如何解决:让子类去实现工厂接口,返回一个抽象的父类。

一:那么我们先来看一个例子:我们如何实现一个简单的计算器的功能呢??首先我们先来看运算类,代码如下:

    /// <summary>
    /// 运算类
    /// </summary>
    public class Operation
    {
        /// <summary>
        /// 得到结果的方法
        /// </summary>
        /// <param name="numberA">输入的数字a</param>
        /// <param name="numberB">输入的数字b</param>
        /// <param name="operate">输入的指令</param>
        /// <returns>返回结果</returns>
        public static double GetResule(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 = Convert.ToString(Operation.GetResule(Convert.ToDouble(strNumberA), Convert.ToDouble(strNumberB), strOperate));
                Console.WriteLine("结果是:{0}", strResult);
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("你输入的有误:" + ex.Message);
            }
        }
    }

这时候我们需要考虑一个问题:假如在我在增加一个开根运算,那么我们如何修改呢??问题是:我们要加一个开根的运算,需要让加减乘除都参与进来进行编译。比如我们来打一个比方:如果现在公司要求你为公司的薪资管理系统进行维护,原来只有技术人员,市场的销售人员,经理,这个时候我们现在在加入一个兼职工作人员的工资,按照我们现在的这种写法,公司就得需要把包含前三种的运算类的编码都给你,这样的话是不是显得有点不合适呢。

在这个时候我们可不可以这样做:我们应该把加减乘除类全部都分离开来,我们修改其中的一个,都不会影响到其他的运算类。

这个时候面向对象的思想就显得尤为重要了:我们可以使用封装 继承 多态可以把程序的耦合度降低,并且使用设计模式使得程序更加的灵活,容易修改,并且易于复用,我们总结的四个面向对象的好处是:(1)可维护 (2) 可复用  (3)可扩展 (4)灵活性好。    

这时候我们考虑应该如何做呢?

既然我们决定把加减乘除都分离开来都写成一个单独的类来进行存储。我们需要考虑他们这四个类中都需要什么(或者有什么共同的东西)。

1.他们四个类都需要有两个数字(NumberA和NumberB)。

2.他们都需要一个得到计算之后的结果。

这样的话我们可以声明一个抽象的父类已供子类继承使用,详细代码如下:

 

    /// <summary>
    /// 抽象方法
    /// </summary>
    public abstract class CalFather
    {
        //数字一
        private double _numberOne;
        //属性用来保护字段
        public double NumberOne
        {
            get
            {
                return _numberOne;
            }

            set
            {
                _numberOne = value;
            }
        }
        //数字二
        private double _numberTwo;
        //属性的设置用来保护字段
        public double NumberTwo
        {
            get
            {
                return _numberTwo;
            }

            set
            {
                _numberTwo = value;
            }
        }
        //抽象的方法
        public abstract double GetResult();
        /// <summary>
        /// 父类自己的构造函数,进行传值使用。
        /// </summary>
        /// <param name="numberOne">第一个数字</param>
        /// <param name="numberTwo">第二个数字</param>
        public CalFather(double n1, double n2)
        {
            this.NumberOne = n1;
            this.NumberTwo = n2;
        }
    }


这样的话我们就可以在子类中重写父类的抽象的方法了。详细代码如下:

    /// <summary>
    /// 加法
    /// </summary>
    public class Jia : CalFather
    {
        //继承父类得到父类的字段
        public Jia(double n1, double n2) : base(n1, n2)
        {

        }
        /// <summary>
        /// 方法的重写
        /// </summary>
        /// <returns>总和</returns>
        public override double GetResult()
        {
            return this.NumberOne + this.NumberTwo;
        }
    }
    /// <summary>
    /// 减法
    /// </summary>
    public class Jian : CalFather
    {
        public Jian(double n1, double n2) : base(n1, n2)
        {

        }
        /// <summary>
        /// 方法的重写
        /// </summary>
        /// <returns>总和</returns>
        public override double GetResult()
        {
            return this.NumberOne - this.NumberTwo;
        }
    }
    /// <summary>
    /// 乘法
    /// </summary>
    public class Cheng : CalFather
    {
        public Cheng(double n1, double n2) : base(n1, n2)
        {

        }
        /// <summary>
        /// 方法的重写
        /// </summary>
        /// <returns>总和</returns>
        public override double GetResult()
        {
            return this.NumberOne * this.NumberTwo;
        }
    }
    /// <summary>
    /// 除法
    /// </summary>
    public class Chu : CalFather
    {
        public Chu(double n1, double n2) : base(n1, n2)
        {

        }
        /// <summary>
        /// 方法的重写
        /// </summary>
        /// <returns>总和</returns>
        public override double GetResult()
        {
            return this.NumberOne / this.NumberTwo;
        }
    }

 


当编写完上面的代码之后,我们该考虑如何的实例化对象的问题了。这时候我们的“简单工厂模式”就派上用场了。

简单工厂的最主要的一点是:返回父类。

为什们要返回父类呢?这里用到了里式转换的思想。

(1):子类可以赋值给父类。

(2):如果父类中装的是子类对象,那么可以将这个父类强转为子类对象。

代码如下:

        /// <summary>
        /// 简单工厂的核心
        /// </summary>
        /// <param name="opera">输入的运算符</param>
        /// <param name="n1">第一个数字</param>
        /// <param name="n2">第二个数字</param>
        /// <returns>进行父类的重写的方法</returns>
        public static CalFather GetCal(string opera, int n1, int n2)
        {
            CalFather cal = null;
            switch (opera)
            {
                case "+":
                    cal = new Jia(n1, n2);
                    break;
                case "-":
                    cal = new Jian(n1, n2);
                    break;
                case "*":
                    cal = new Cheng(n1, n2);
                    break;
                case "/":
                    cal = new Chu(n1, n2);
                    break;
            }
            return cal;
        }
    }

这样的话我们在增加各种复杂的运算,比如平方根,立方根,这个时候我们只需要添加相应的子类在继承父类就可以了,在修改共产添加分支就行了。而我们之前实现的代码没有进行任何的修改。这也体现了面向对象的好处。

简单工厂模式的优缺点:

  • 简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端可以消除直接创建对象的责任,而仅仅是消费产品。简单工厂模式实现了对责任的分割。
  • 简单工厂模式也起到了代码复用的作用,(同时这点也是简单工厂方法的缺点——因为工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响,也没什么不好理解的,就如事物都有两面性一样道理)

虽然上面已经介绍了简单工厂模式的缺点,下面还是总结下简单工厂模式的缺点:

  • 工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,这样就会造成工厂逻辑过于复杂。

了解了简单工厂模式之后的优缺点之后,我们之后就可以知道简单工厂的应用场景了:

  • 当工厂类负责创建的对象比较少时可以考虑使用简单工厂模式()
  • 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时可以考虑使用简单工厂模式

好了,本次的讲解就到这里了,大家有什么问题可以积极的留言。谢谢大家。

 

posted @ 2017-10-22 23:50  丢了蜡笔小新会哭〆  阅读(220)  评论(0编辑  收藏  举报