C#回顾 - 7.如何使用反射实现工厂模式?

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

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

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

C#回顾 - 7.如何使用反射实现工厂模式?

上图的设计思路是:

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

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

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

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

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

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

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

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

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

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

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. /// <summary>
  13. /// 窗户工厂
  14. /// </summary>
  15. public class WindowFactory : IFactory
  16. {
  17. // 实现接口,返回一个产品对象
  18. public IProduct Produce()
  19. {
  20. return new Window();
  21. }
  22. }
  23. /// <summary>
  24. /// 柱子工厂
  25. /// </summary>
  26. public class PillarFactory : IFactory
  27. {
  28. // 实现接口,返回一个产品对象
  29. public IProduct Produce()
  30. {
  31. return new Pillar();
  32. }
  33. }

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

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. Console.ReadKey();
  12. }
  13. }

C#回顾 - 7.如何使用反射实现工厂模式?

 当一个新的产品—地板需要被添加时,我们需要改的地方是:添加零件枚举记录、添加针对地板的工厂类、添加新地板产品类,修改工厂管理类(在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. public ProductAttribute(RoomParts part)
  10. {
  11. myRoomPart = part;
  12. }
  13. public RoomParts RoomPart
  14. {
  15. get
  16. {
  17. return myRoomPart;
  18. }
  19. }
  20. }
  21. /// <summary>
  22. /// 该特性用于附加在产品接口类型之上
  23. /// </summary>
  24. [AttributeUsage(AttributeTargets.Interface)]
  25. public class ProductListAttribute : Attribute
  26. {
  27. // 产品类型集合
  28. private Type[] myList;
  29. public ProductListAttribute(Type[] products)
  30. {
  31. myList = products;
  32. }
  33. public Type[] ProductList
  34. {
  35. get
  36. {
  37. return myList;
  38. }
  39. }
  40. }

②下面是产品接口和产品类族的定义,其中产品接口使用了 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. /// <summary>
  14. /// 窗户
  15. /// </summary>
  16. [Product(RoomParts.Window)]
  17. public class Window : IProduct
  18. {
  19. // 实现接口,返回产品名字
  20. public string GetName()
  21. {
  22. return "双汇窗户";
  23. }
  24. }
  25. /// <summary>
  26. /// 柱子
  27. /// </summary>
  28. [Product(RoomParts.Pillar)]
  29. public class Pillar : IProduct
  30. {
  31. // 实现接口,返回产品名字
  32. public string GetName()
  33. {
  34. return "小米柱子";
  35. }
  36. }

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

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. return null;
  18. }
  19. }
  20. ///// <summary>
  21. ///// 屋顶工厂
  22. ///// </summary>
  23. //public class RoofFactory : IFactory
  24. //{
  25. //    // 实现接口,返回一个产品对象
  26. //    public IProduct Produce()
  27. //    {
  28. //        return new Roof();
  29. //    }
  30. //}
  31. ... ...

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

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. Factory factory = new Factory();
  23. IProduct product = factory.Product(part);
  24. Console.WriteLine("生产了一个产品:{0}", product.GetName());
  25. return product;
  26. }
  27. }

上述代码中最主要的变化在于两点:
其一是工厂管理类不再需要根据不同的零件寻找不同的工厂,因为只有一个工厂负责处理所有的产品零件;
其二是产品类型和产品接口应用了两个自定义特性,来方便工厂进行反射。
 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

 
上一篇:jquery.datatable.js与CI整合 异步加载(大数据量处理)


下一篇:Oracle普通表->分区表转换(9亿数据量)