zoukankan      html  css  js  c++  java
  • C#回顾

    工厂模式是一种比较常用的设计模式,其基本思想在于使用不同的工厂类型来打造不同产品的部件。例如,我们在打造一间屋子时,可能需要窗户、屋顶、门、房梁、柱子等零部件。有的屋子需要很多根柱子,而有的屋子又不需要窗户。在这样的需求下,就可以使用工厂模式。

    (1)工厂模式的传统实现和其弊端

      下图展示了针对屋子设计的传统工厂模式架构图:

    上图的设计思路是:

      ①使用者告诉工厂管理者需要哪个产品部件;

      ②工厂管理者分析使用者传入的信息,生成合适的实现工厂接口的类型对象;

      ③通过工厂生产出相应的产品,返回给使用者一个实现了该产品接口的类型对象;


      通过上述思路,实现代码如下:

      ①首先是定义工厂接口,产品接口与产品类型的枚举

    IFactory.cs(工厂接口,产品接口一起

    1. /// <summary>  
    2. /// 屋子产品的零件  
    3. /// </summary>  
    4. public enum RoomParts  
    5. {  
    6.     Roof,  
    7.     Window,  
    8.     Pillar  
    9. }  
    10.   
    11. /// <summary>  
    12. /// 工厂接口  
    13. /// </summary>  
    14. public interface IFactory  
    15. {  
    16.     IProduct Produce();  
    17. }  
    18.   
    19. /// <summary>  
    20. /// 产品接口  
    21. /// </summary>  
    22. public interface IProduct  
    23. {  
    24.     string GetName();  
    25. }  

    ②其次是具体实现产品接口的产品类:窗户、屋顶和柱子

    Product.cs

    1. /// <summary>  
    2.  /// 屋顶  
    3.  /// </summary>  
    4.  public class Roof : IProduct  
    5.  {  
    6.      // 实现接口,返回产品名字  
    7.      public string GetName()  
    8.      {  
    9.          return "屋顶";  
    10.      }  
    11.  }  
    12.   
    13.  /// <summary>  
    14.  /// 窗户  
    15.  /// </summary>  
    16.  public class Window : IProduct  
    17.  {  
    18.      // 实现接口,返回产品名字  
    19.      public string GetName()  
    20.      {  
    21.          return "窗户";  
    22.      }  
    23.  }  
    24.   
    25.  /// <summary>  
    26.  /// 柱子  
    27.  /// </summary>  
    28.  public class Pillar : IProduct  
    29.  {  
    30.      // 实现接口,返回产品名字  
    31.      public string GetName()  
    32.      {  
    33.          return "柱子";  
    34.      }  
    35.  }  

    ③然后是具体实现工厂接口的工厂类:实现接口返回一个具体的产品对象

    Factory.cs

    1. /// <summary>  
    2.  /// 屋顶工厂  
    3.  /// </summary>  
    4.  public class RoofFactory : IFactory  
    5.  {  
    6.      // 实现接口,返回一个产品对象  
    7.      public IProduct Produce()  
    8.      {  
    9.          return new Roof();  
    10.      }  
    11.  }  
    12.   
    13.  /// <summary>  
    14.  /// 窗户工厂  
    15.  /// </summary>  
    16.  public class WindowFactory : IFactory  
    17.  {  
    18.      // 实现接口,返回一个产品对象  
    19.      public IProduct Produce()  
    20.      {  
    21.          return new Window();  
    22.      }  
    23.  }  
    24.   
    25.  /// <summary>  
    26.  /// 柱子工厂  
    27.  /// </summary>  
    28.  public class PillarFactory : IFactory  
    29.  {  
    30.      // 实现接口,返回一个产品对象  
    31.      public IProduct Produce()  
    32.      {  
    33.          return new Pillar();  
    34.      }  
    35.  }  

     ④最后是工厂管理类:组织起众多的产品与工厂

    FactoryManager.cs

    1. class FactoryManager  
    2. {  
    3.     public static IProduct GetProduct(RoomParts part)  
    4.     {  
    5.         IFactory factory = null;  
    6.         //传统工厂模式的弊端,工厂管理类和工厂类的紧耦合  
    7.         switch (part)  
    8.         {  
    9.                 case RoomParts.Roof:  
    10.                 factory = new RoofFactory();  
    11.                 break;  
    12.             case RoomParts.Window:  
    13.                 factory = new WindowFactory();  
    14.                 break;  
    15.                 case RoomParts.Pillar:  
    16.                factory = new PillarFactory();  
    17.                 break;  
    18.             default:  
    19.                 return null;  
    20.         }  
    21.         IProduct product = factory.Produce();  
    22.         Console.WriteLine("生产了一个产品:{0}", product.GetName());  
    23.         return product;  
    24.     }  
    25. }  

    按照国际惯例,我们实现一个入口方法来测试一下:

    Client.cs

    1. class Client  
    2. {  
    3.     static void Main(string[] args)  
    4.     {  
    5.         IProduct window = FactoryManager.GetProduct(RoomParts.Window);  
    6.         Console.WriteLine("我获取到了{0}", window.GetName());  
    7.         IProduct roof = FactoryManager.GetProduct(RoomParts.Roof);  
    8.         Console.WriteLine("我获取到了{0}", roof.GetName());  
    9.         IProduct pillar = FactoryManager.GetProduct(RoomParts.Pillar);  
    10.         Console.WriteLine("我获取到了{0}", pillar.GetName());  
    11.   
    12.         Console.ReadKey();  
    13.     }  
    14. }  



     当一个新的产品—地板需要被添加时,我们需要改的地方是:添加零件枚举记录、添加针对地板的工厂类、添加新地板产品类,修改工厂管理类(在switch中添加一条case语句),这样设计的优点在于无论添加何种零件,产品使用者都不需要关心内部的变动,可以一如既往地使用工厂管理类来得到希望的零件,而缺点也有以下几点:

      ①工厂管理类和工厂类族耦合;

      ②每次添加新的零件都需要添加一对工厂类和产品类,类型会越来越多;

      

    (2)基于反射的工厂模式的实现

      利用反射机制可以实现更加灵活的工厂模式,这一点体现在利用反射可以动态地获知一个产品由哪些零部件组成,而不再需要用一个switch语句来逐一地寻找合适的工厂。

    ①产品、枚举和以上一致,这里的改变主要在于添加了两个自定义的特性,这两个特性会被分别附加在产品类型和产品接口上:
    ProductAttribute.cs

    1. /// <summary>  
    2. /// 该特性用于附加在产品类型之上  
    3. /// </summary>  
    4. [AttributeUsage(AttributeTargets.Class)]  
    5. public class ProductAttribute : Attribute  
    6. {  
    7.     // 标注零件的成员  
    8.     private RoomParts myRoomPart;  
    9.   
    10.     public ProductAttribute(RoomParts part)  
    11.     {  
    12.         myRoomPart = part;  
    13.     }  
    14.   
    15.     public RoomParts RoomPart  
    16.     {  
    17.         get  
    18.         {  
    19.             return myRoomPart;  
    20.         }  
    21.     }  
    22. }  
    23.   
    24. /// <summary>  
    25. /// 该特性用于附加在产品接口类型之上  
    26. /// </summary>  
    27. [AttributeUsage(AttributeTargets.Interface)]  
    28. public class ProductListAttribute : Attribute  
    29. {  
    30.     // 产品类型集合  
    31.     private Type[] myList;  
    32.   
    33.     public ProductListAttribute(Type[] products)  
    34.     {  
    35.         myList = products;  
    36.     }  
    37.   
    38.     public Type[] ProductList  
    39.     {  
    40.         get  
    41.         {  
    42.             return myList;  
    43.         }  
    44.     }  
    45. }  


    ②下面是产品接口和产品类族的定义,其中产品接口使用了 ProductListAttribute 特性,而每个产品都使用了 ProductAttribute 特性:

    1. /// <summary>  
    2. /// 屋顶  
    3. /// </summary>  
    4. [Product(RoomParts.Roof)]  
    5. public class Roof : IProduct  
    6. {  
    7.     // 实现接口,返回产品名字  
    8.     public string GetName()  
    9.     {  
    10.         return "小天鹅屋顶";  
    11.     }  
    12. }  
    13.   
    14. /// <summary>  
    15. /// 窗户  
    16. /// </summary>  
    17. [Product(RoomParts.Window)]  
    18. public class Window : IProduct  
    19. {  
    20.     // 实现接口,返回产品名字  
    21.     public string GetName()  
    22.     {  
    23.         return "双汇窗户";  
    24.     }  
    25. }  
    26.   
    27. /// <summary>  
    28. /// 柱子  
    29. /// </summary>  
    30. [Product(RoomParts.Pillar)]  
    31. public class Pillar : IProduct  
    32. {  
    33.     // 实现接口,返回产品名字  
    34.     public string GetName()  
    35.     {  
    36.         return "小米柱子";  
    37.     }  
    38. }  

    ③下面是修改后的工厂类,由于使用了反射特性,这里一个工厂类型就可以生产所有的产品:

    Factory.cs

    1. public class Factory  
    2. {  
    3.     public IProduct Product(RoomParts parts)  
    4.     {  
    5.         ProductListAttribute attr =  
    6.             (ProductListAttribute)System.Attribute.GetCustomAttribute(typeof(IProduct), typeof(ProductListAttribute));  
    7.         foreach (var type in attr.ProductList)  
    8.         {  
    9.             ProductAttribute pa =  
    10.                 (ProductAttribute)System.Attribute.GetCustomAttribute(type, typeof(ProductAttribute));  
    11.             if (pa.RoomPart == parts)  
    12.             {  
    13.                 object product = Assembly.GetExecutingAssembly().CreateInstance(type.FullName);  
    14.                 return product as IProduct;  
    15.             }  
    16.         }  
    17.   
    18.         return null;  
    19.     }  
    20. }  
    21.   
    22. ///// <summary>  
    23. ///// 屋顶工厂  
    24. ///// </summary>  
    25. //public class RoofFactory : IFactory  
    26. //{  
    27. //    // 实现接口,返回一个产品对象  
    28. //    public IProduct Produce()  
    29. //    {  
    30. //        return new Roof();  
    31. //    }  
    32. //}  
    33.   
    34. ... ...

    ④最后时修改后的工厂管理类,核心只有三行代码:

    FactoryManager.cs

    1. class FactoryManager  
    2.  {  
    3.      public static IProduct GetProduct(RoomParts part)  
    4.      {  
    5.          //IFactory factory = null;  
    6.          ////传统工厂模式的弊端,工厂管理类和工厂类的紧耦合  
    7.          //switch (part)  
    8.          //{  
    9.          //        case RoomParts.Roof:  
    10.          //        factory = new RoofFactory();  
    11.          //        break;  
    12.          //    case RoomParts.Window:  
    13.          //        factory = new WindowFactory();  
    14.          //        break;  
    15.          //        case RoomParts.Pillar:  
    16.          //       factory = new PillarFactory();  
    17.          //        break;  
    18.          //    default:  
    19.          //        return null;  
    20.          //}  
    21.          //IProduct product = factory.Produce();  
    22.   
    23.          Factory factory = new Factory();  
    24.          IProduct product = factory.Product(part);  
    25.          Console.WriteLine("生产了一个产品:{0}", product.GetName());  
    26.          return product;  
    27.      }  
    28.  }  

    上述代码中最主要的变化在于两点:
    其一是工厂管理类不再需要根据不同的零件寻找不同的工厂,因为只有一个工厂负责处理所有的产品零件;
    其二是产品类型和产品接口应用了两个自定义特性,来方便工厂进行反射。
     ProductAttribute 附加在产品类上,标注了当前类型代表了哪个产品零件。而 ProductListAttribute 则附加在产品接口之上,方便反射得知一共有多少产品零件。
    这时需要添加一个新的地板产品零件类型时,我们需要做的是:
    1.添加零件枚举记录
    1. /// <summary>  
    2. /// 屋子产品的零件  
    3. /// </summary>  
    4. public enum RoomParts  
    5. {  
    6.     Roof,  
    7.     Window,  
    8.     Pillar,  
    9.     Floor //地板  
    10. }  
    2.添加代表地板的类型(Product.cs)
    1. /// <summary>  
    2. /// 地板  
    3. /// </summary>  
    4. [Product(RoomParts.Floor)]  
    5. public class Floor : IProduct  
    6. {  
    7.     // 实现接口,返回产品名字  
    8.     public string GetName()  
    9.     {  
    10.         return "地板";  
    11.     }  
    12. }  
    3.修改添加在IProduct上的属性初始化参数(增加地板类型),
    1.     /// <summary>  
    2.     /// 产品接口  
    3.     /// </summary>  
    4.     [ProductList(new Type[] { typeof(Roof), typeof(Window), typeof(Pillar)typeof(Floor) })]  
    5.     public interface IProduct  
    6.     {  
    7.         string GetName();  
    8.     }  
    可以看到这时调用者、工厂管理类和工厂都不再需要对新添加的零件进行改动,程序只需要添加必要的类型和枚举记录即可。当然,这样的设计也存在一定缺陷:反射的运行效率相对较低,在产品零件相对较多时,每生产一个产品就需要反射遍历这是一件相当耗时的工作。


    作者:周旭龙

    出处:http://www.cnblogs.com/edisonchou/p/4827578.html


     



  • 相关阅读:
    Windows 右键添加用记事本打开的选项
    shell 脚本常用写法
    常用命令--dig
    电子表格数字式的小时化成时分秒格式
    Kaggle猫狗图像分类竞赛Baseline
    阿里巴巴用户体验研究专员暑期实习生笔试 经验分享 2019
    sysctl -w net.core.somaxconn=65535
    src/stream/ngx_stream_proxy_module.c:542: 错误:‘ngx_stream_upstream_t’没有名为‘ssl_name’的成员
    ssl.cpp:333: error: ‘SSL_set_tlsext_host_name’ was not declared in this scope
    fiddler QuickExec
  • 原文地址:https://www.cnblogs.com/tangge/p/6109820.html
Copyright © 2011-2022 走看看