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

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

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

上图的设计思路是:

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

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

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

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

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

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. }

 当一个新的产品—地板需要被添加时,我们需要改的地方是:添加零件枚举记录、添加针对地板的工厂类、添加新地板产品类,修改工厂管理类(在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

 

C#回顾 - 7.如何使用反射实现工厂模式?的更多相关文章

  1. java反射机制(工厂模式)

    http://www.phpddt.com/dhtml/338.html java里面没有typeof,js有. 我终于实现了用反射机制编写的工厂模式.java反射在工厂模式可以体现. 包含产品接口类 ...

  2. IOC的实现原理—反射与工厂模式的结合

    反射机制概念   我们考虑一个场景,如果我们在程序运行时,一个对象想要检视自己所拥有的成员属性,该如何操作?再考虑另一个场景,如果我们想要在运行期获得某个类的Class信息如它的属性.构造方法.一般方 ...

  3. java学习笔记之反射—反射和工厂模式

    简单工厂模式又称为静态工厂方法模式,它是由工厂对象来决定要创建哪一种类的实例化对象. 静态工厂代码: class Factory{ private Factory() {} public static ...

  4. Java反射+简单工厂模式总结

    除了 new 之外的创建对象的方法 通过 new 创建对象,会使得程序面向实现编程,先举个例子,某个果园里现在有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用get()方法表示即可 ...

  5. C# 反射+抽象工厂模式

    此模式可以很好的更换程序使用不同的数据库 1.用到的属性类 using System; using System.Collections.Generic; using System.Linq; usi ...

  6. java 反射的应用 以及通过反射 用到的工厂模式

    java反射详解 本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案 ...

  7. Java反射机制在工厂模式中的应用

    在本篇文章中就不详细介绍工厂模式,主要介绍一下反射在工厂模式中的使用,让读者对反射机制带来的好处有更深的认识. 首先看一下简单工厂模式 简单工厂模式(simple factory)是类的创建模式,又叫 ...

  8. 反射 + 抽象工厂模式切换DB数据源(附Demo)

    首先,设计模式的文章源自于程杰的<大话设计模式>这本书,这本书个人感觉很适合我,看着不累,能够安安心心的阅读学习.在这里十分感谢程杰的这本书,我博文中的例子会根据书上的例子来.为了不侵犯这 ...

  9. java 设计模式之工厂模式与反射的结合

    工厂模式: /**  * @author Rollen-Holt 设计模式之 工厂模式  */   interface fruit{     public abstract void eat(); } ...

随机推荐

  1. lambda与常用内置函数

    lambda表达式: lambda arg:arg+1 数值操作: abs() 求绝对值 abs(-1) bin() 将十进制转换成二进制   bin(3) ,’0b11’ hex() 十进制转换为十 ...

  2. MySQL多配置方式的多实例的部署

    安装MySQL需要注意的事项: 选择MySQL的版本的建议: 1)稳定版:选择开源的社区版的稳定版GA版本 2)选择MySQL数据库GA版本发布后六个月以后得GA版本 3)选择发布版本前后几个月没有大 ...

  3. jQuery插件 -- Cookie插件jquery.cookie.js(转)

    Cookie是网站设计者放置在客户端的小文本文件.Cookie能为用户提供很多的使得,例如购物网站存储用户曾经浏览过的产品列表,或者门户网站记住用户喜欢选择浏览哪类新闻. 在用户允许的情况下,还可以存 ...

  4. mogodb3.2源码安装

    mogodb3.2源码安装 下载链接: http://www.mongodb.org/downloads 1.环境准备: 1.mkdir -p /data/tgz #创建存放软件的目录 2.mkdir ...

  5. openerp7 时区问题

    由于目前openerp 的时区,读取的是UTC 时间,而我国本地时间比UTC 快8小时,这个问题就导致:写入数据库的时候时间相差8小时,以及Openerp日志输出时间格式也相差8小时和 前端显示时间的 ...

  6. 运维请注意:”非常危险“的Linux命令大全

    Linux命令是一种很有趣且有用的东西,但在你不知道会带来什么后果的时候,它又会显得非常危险.所以,在输入某些命令前,请多多检查再敲回车. rm –rf rm –rf是删除文件夹和里面附带内容的一种最 ...

  7. How to create vlan on Linux (with Cisco Catalyst Switch)

    In this article I want to share to you on how to create and configure vlan on Linux through Cisco Ca ...

  8. [Web开发] 在HTML代码里面如何判断IE版本

    在上一篇blog里面提到IE有不同的显示模式以及如何用Javascript 来动态判定. Web开发者可以根据不同显示模式导入不同的内容.这篇blog 主要讲如何让静态HTML代码根据不同IE版本显示 ...

  9. centos7 没有iptables服务 file or directory? 用secureCRT登录centos?

    cenetos7 采用systemd来管理服务 centos7 没有采用传统的iptables服务, 而是采用的firewalld 服务, 以及firewall-cmd 命令; 也可以采用传统的 ip ...

  10. javascript数据结构与算法-- 二叉树

    javascript数据结构与算法-- 二叉树 树是计算机科学中经常用到的一种数据结构.树是一种非线性的数据结构,以分成的方式存储数据,树被用来存储具有层级关系的数据,比如文件系统的文件,树还被用来存 ...