zoukankan      html  css  js  c++  java
  • C#学习之设计模式:工厂模式

      最近研究一下设计模式中工厂模式的应用,在此记录如下:

      什么是工厂模式?

      工厂模式属于设计模式中的创造型设计模式的一种。它的主要作用是协助我们创建对象,为创建对象提供最佳的方式。减少代码中的耦合程度,方便后期代码的维护。

      工厂模式又可以详细细分为简单工厂模式,工厂方法模式和抽象工厂模式。下面我们一一道来。

      简单工厂模式

      严格意义上来说,简单工厂模式并不能计入设计模式大家族。

      因为它并不严格符合设计模式要求的对扩展开放,对修改关闭的设计原则。但是简单工厂模式简单好用,行之有效,在小型项目或简单的代码逻辑中使用它的人并不少。

      定义:

    简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。

      类图:

     image

      其中:

      工厂(Creator)角色是 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
      抽象产品(Product)角色 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
      具体产品(Concrete Product)角色 是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

      通俗理解就是:Creator是工厂类,通过它进行条件选择决定实例化哪一个具体的product,并返回接口Iproduct,客户端直接使用Iproduct即可,无需了解Iproduct到底是什么类。

      适用范围和适用场景

    1. 工厂类负责创建的对象比较少;
    2. 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
    3. 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。

      优点:

        简单直接,易于理解,减少了客户端的条件判断。

        缺点:

      1.由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的类。

      2.如果需要添加新的类,则就需要改变工厂类了。 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;

          代码示例

    //客户端,客户端调用时只与简单工厂类进行交互,客户端不知道也不关心工厂类返回给它的是哪一个具体的类,只要知道这个类能够完成的功能有哪些即可
        class Program
        {
            static void Main(string[] args)
            {
                string DataBaseType = "SqlServer";
                Factory factory = new Factory(DataBaseType);
                factory.IDataBase.Select();
                factory.IDataBase.Insert();
    
                Console.ReadKey();
    
            }
        }
        //数据库接口,相当于图中的Iproduct。通过定义接口,方便客户端知道如何调用工厂方法返回的类
        public interface IDataBase
        {
            void Select();
            void Insert();
        }
        
        
        
        //具体的产品类,满足接口约束之后,完成各自不同的动作,相当于图中的product_A,product_B等
        
        public class Oracle : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行Oracle类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行Oracle类的Select");
            }
        }
        
        
        public class Access : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行Access类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行Access类的Select");
            }
        }
        
        public class SqlServer : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行sqlServer类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行sqlServer类的Select");
            }
        }
        
        
        //简单工厂类,通过条件分支判断具体应该实例化哪一个类。相当于图中的Creator
        
        public class Factory
        {
            public IDataBase IDataBase { get; set; }
            public Factory(string DbType)
            {
                switch (DbType)
                {
                    case "SqlServer":
                        IDataBase = new SqlServer();
                        break;
                    case "Oracle":
                        IDataBase = new Oracle();
                        break;
                    case "Access":
                        IDataBase = new Access();
                        break;
                }
            }
        }


      简单工厂模式,我非常喜欢用,它足够简单,代码量小,但是有效。通常,我们国土行业内部系统中会出现类似于这样的代码。

    class BadCode
        {
            string GetBadCode(string DataBaseType)
            {
                string Result = "";
                if (DataBaseType == "Oracle")
                {
                    /*此处大量代码(数十行)运算获得从oracle数据库中获取的结果Result*/
                }
                else if (DataBaseType == "Access")
                {
                    /*此处大量代码(数十行)运算获得从Access数据库中获取的结果Result*/
                }
                else if (DataBaseType == "SqlServer")
                {
                    /*此处大量代码(数十行)运算获得从SqlServer数据库中获取的结果Result*/
                }
                return Result;
            }
        }

      这样的代码阅读起来非常费劲,如果想要修改这样的代码,也是非常彷徨的。如果把每个条件分支中的代码稍作整理封装到类中,并提取公共接口,采用简单工厂模式组织的话,代码的清晰度必然有大的提升。

      工厂方法模式

      简单工厂高效,好用,但是它只适合简单的有固定的待创建对象列表的情况,不然反复修改工厂类中的case分支很麻烦,也不符合软件设计的开闭原则。

      通过分析人们发现简单工厂模式主要是工厂类与判断分支耦合了,那么就从这个地方下手,根据依赖倒转原则把工厂类抽象出一个接口,将具体的实例化方法延迟到接口的子类来实现。工厂方法模式就出现了。

      定义:

    定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类

      类图:

    image

      它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品

      抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。

      具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。

      抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。

      具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

      抽象工厂不再负责直接创建具体的对象,它只定义了一个生成抽象产品的接口,由其子类去生产具体的产品。这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

      简单的说就是:用户调用Creator接口,并实例化为一个具体的Concrete Creator,Concrete Creator返回一个Product给用户使用。用户无需知道Product是哪一个具体的Concrete Product。Product也不关心是谁在调用它。在需要更换Concrete Creator时,因为采用了依赖注入的原则设计,也只需要将Creator实例化为另一个Concrete Creator,其他地方无需修改。

      适用范围和适用场景

      工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。 适用于单个产品分类多个产品的情况,或者用户知道自己需要实例化哪一个具体的类,但是这个具体的类可能需要经常变化的情况。

      代码示例

    class Program
        {
            static void Main(string[] args)
            {
                IFactory factory = new SqlServerFactory();
                IDataBase dataBase = factory.CreateDataBase();
                dataBase.Insert();
                dataBase.Select();
                Console.ReadKey();
    
            }
        }
        
        public interface IFactory
        {
            IDataBase CreateDataBase();
        }
        
        public interface IDataBase
        {
            void Select();
            void Insert();
        }
        
        public class OracleFactory : IFactory
        {
            public IDataBase CreateDataBase()
            {
                return new Oracle();
            }
        }
        
        public class AccessFactory : IFactory
        {
            public IDataBase CreateDataBase()
            {
                return new Access();
            }
        }
        
        public class SqlServerFactory : IFactory
        {
            public IDataBase CreateDataBase()
            {
                return new SqlServer();
            }
        }
        
        public class SqlServer : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行sqlServer类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行sqlServer类的Select");
            }
        }
        
        public class Oracle : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行Oracle类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行Oracle类的Select");
            }
        }
        
        public class Access : IDataBase
        {
            public void Insert()
            {
                Console.WriteLine("执行Access类的Insert");
            }
    
            public void Select()
            {
                Console.WriteLine("执行Access类的Select");
            }
        }

      优点:

      1.需要增加待生成的子类对象时,只需要添加子类和相应的工厂即可,无需修改已有代码,符合开闭设计原则。

      2.在于通过依赖注入可以很轻松的实现具体产品类的更换,而无需修改大量的代码,如果配合反射甚至可以做到无需修改代码。

      缺点:

      将必要的判断移到了客户端调用方,导致如果需要根据条件判断初始化哪个子类的条件判断暴露给了调用方。因此一般工厂方法模式适用于已知需要初始化的类,但此类可能经常需要变化的情况。当然这个缺陷可以通过反射来进行解决。

      抽象工厂模式

      抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。

      定义

    为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

      类图:

    image

      核心角色为:

      抽象工厂(AbstractFactory),是抽象工厂模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。与工厂方法模式不同的是,抽象工厂模式的抽象工厂定义了至少两组不同的抽象产品的创建方法。

      具体工厂(Concrete Creator):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。与工厂方法模式不同的是,抽象工厂模式的具体工厂实现了至少两组不同的抽象产品的创建方法。

      抽象产品(AbstractProduct)角色:抽象工厂模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
      具体产品(ConcreteProduct)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

      适用范围和适用场景

      1.当需要创建产品家族,或者需要想让创建的产品集合起来时使用。

      2.当系列产品不固定,在以后可能会增加整个系列产品时使用。

      优点:

      抽象工厂模式的优点和工厂方法模式的优点基本类似,但是抽象工厂模式更擅长于处理多个产品线的情况,通俗的说就是当需要同时初始化两种及以上不同类别的对象的时候,使用抽象工厂模式相对于工厂方法模式可以少写很多具体工厂类,能够更好的管理代码。

      缺点:

      抽象工厂模式不是万能的。抽象工厂模式的缺点和工厂方法模式的却点也基本类似。但是它还有一个独有的缺点:它特别不适用于产品系列不固定的情况,它可以很容易的添加ConcreteProduct以及Concrete Creator,但是它在添加AbstractProduct的时候需要修改大量的现有类,不符合开闭原则。

      代码示例

    /// <summary>
        /// 抽象工厂,两条“产品线”,采矿权和探矿权
        /// </summary>
        public interface IFactory
        {
            ICKQ CreateCKQ();
            ITKQ CreateTKQ();
    
        }
        
        public class XLFactory : IFactory
        {
            public ICKQ CreateCKQ()
            {
                return new CKQXL();
            }
    
            public ITKQ CreateTKQ()
            {
                return new TKQXL();
            }
        }
        
        public class BGFactory : IFactory
        {
            public ICKQ CreateCKQ()
            {
                return new  CKQBG();
            }
    
            public ITKQ CreateTKQ()
            {
                return new TKQBG();
            }
        }
        
        /// <summary>
        /// 采矿权“产品线”下的“产品”
        /// </summary>
        public interface ICKQ
        {
            //签收
            void Sign();
            //办理完成
            void Finish();
            //保存
            void Save();
        }
        
        /// <summary>
        /// 探矿权“产品线”下的“产品”
        /// </summary>
        public interface ITKQ
        {
            void Sign();
            void Finish();
            void Save();
        }
        
        public class CKQXL : ICKQ
        {
            public void Finish()
            {
                Console.WriteLine("实现采矿权新立的办理完成");
            }
    
            public void Save()
            {
                Console.WriteLine("实现采矿权新立的保存");
            }
    
            public void Sign()
            {
                Console.WriteLine("实现采矿权新立的签收");
            }
        }
        
        public class CKQBG : ICKQ
        {
            public void Finish()
            {
                Console.WriteLine("实现采矿权变更的办理完成");
            }
    
            public void Save()
            {
                Console.WriteLine("实现采矿权变更的办理完成");
            }
    
            public void Sign()
            {
                Console.WriteLine("实现采矿权变更的办理完成");
            }
        }
        
        public class TKQBG: ITKQ
        {
            public void Finish()
            {
                Console.WriteLine("实现探矿权变更的办理完成");
            }
    
            public void Save()
            {
                Console.WriteLine("实现探矿权变更的保存");
            }
    
            public void Sign()
            {
                Console.WriteLine("实现探矿权变更的签收");
            }
        }
        
        public class TKQXL:ITKQ
        {
            public void Finish()
            {
                Console.WriteLine("实现探矿权新立的办理完成");
            }
    
            public void Save()
            {
                Console.WriteLine("实现探矿权新立的保存");
            }
    
            public void Sign()
            {
                Console.WriteLine("实现探矿权新立的签收");
            }
        }
        
        static void Main(string[] args)
            {
                IFactory factory = new XLFactory();
                ICKQ ckq = factory.CreateCKQ();
                ckq.Save();
                ckq.Sign();
                ckq.Finish();
                ITKQ tkq = factory.CreateTKQ();
                tkq.Save();
                tkq.Sign();
                tkq.Finish();
                Console.ReadKey();
    
            }

    三种工厂优劣势比较及总结

      一句话来说就是:工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结构。简单工厂模式简单好用,包含判断逻辑,但不适合大型复杂情况。

        个人理解:最好用的是简单工厂,最难懂的是抽象工厂。很多情况下,我们甚至可以将简单工厂模式与工厂方法模式和抽象工厂模式结合起来使用,可能是一个最好的折中的方式。

     

    代码地址:

    https://coding.net/u/wenpeng/p/DesignPattern/git

    引用说明

    1. https://baike.baidu.com/item/简单工厂模式/8801727?fr=aladdin
    2. https://baike.baidu.com/item/设计模式/1212549?fr=aladdin
    3. http://blog.csdn.net/carson_ho/article/details/52343584
    4. 《大话设计模式》 https://book.douban.com/subject/2334288/
    5. https://baike.baidu.com/item/工厂方法模式
    6. https://baike.baidu.com/item/抽象工厂模式
    7. https://www.cnblogs.com/jenkinschan/p/5712874.html
  • 相关阅读:
    107. 二叉树的层次遍历 II
    c#、ASP.NET core 基础模块之一:linq(原创)
    sql 查询 一张表里面的数据 在另一张表中是否存在 和 比对两个集合中的差集和交集(原创)
    winform从table1获取需要的数据转存储到table2中
    Winform学习之随笔一:Log4net
    VS中让用户选择路径
    HttpWebRequest中GetResponse或者说GetRequestStream偶尔超时,或者是各种操作超时造成的假死的一些解决方案
    使用JSON JavaScriptSerializer进行反序列化和序列化时报错,字符的长度超出了MaxJsonLength的长度
    c#中常用集合类和集合接口之接口系列【转】
    c#中常用集合类和集合接口之集合类系列【转】
  • 原文地址:https://www.cnblogs.com/wenpeng/p/8092588.html
Copyright © 2011-2022 走看看