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

  什么是工厂模式?

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

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

  简单工厂模式

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

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

  定义:

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

  类图:

  其中:

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

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

  适用范围和适用场景

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

  优点:

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

    缺点:

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

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

代码示例

  1. //客户端,客户端调用时只与简单工厂类进行交互,客户端不知道也不关心工厂类返回给它的是哪一个具体的类,只要知道这个类能够完成的功能有哪些即可
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. string DataBaseType = "SqlServer";
  7. Factory factory = new Factory(DataBaseType);
  8. factory.IDataBase.Select();
  9. factory.IDataBase.Insert();
  10. Console.ReadKey();
  11. }
  12. }
  13. //数据库接口,相当于图中的Iproduct。通过定义接口,方便客户端知道如何调用工厂方法返回的类
  14. public interface IDataBase
  15. {
  16. void Select();
  17. void Insert();
  18. }
  19. //具体的产品类,满足接口约束之后,完成各自不同的动作,相当于图中的product_A,product_B等
  20. public class Oracle : IDataBase
  21. {
  22. public void Insert()
  23. {
  24. Console.WriteLine("执行Oracle类的Insert");
  25. }
  26. public void Select()
  27. {
  28. Console.WriteLine("执行Oracle类的Select");
  29. }
  30. }
  31. public class Access : IDataBase
  32. {
  33. public void Insert()
  34. {
  35. Console.WriteLine("执行Access类的Insert");
  36. }
  37. public void Select()
  38. {
  39. Console.WriteLine("执行Access类的Select");
  40. }
  41. }
  42. public class SqlServer : IDataBase
  43. {
  44. public void Insert()
  45. {
  46. Console.WriteLine("执行sqlServer类的Insert");
  47. }
  48. public void Select()
  49. {
  50. Console.WriteLine("执行sqlServer类的Select");
  51. }
  52. }
  53. //简单工厂类,通过条件分支判断具体应该实例化哪一个类。相当于图中的Creator
  54. public class Factory
  55. {
  56. public IDataBase IDataBase { get; set; }
  57. public Factory(string DbType)
  58. {
  59. switch (DbType)
  60. {
  61. case "SqlServer":
  62. IDataBase = new SqlServer();
  63. break;
  64. case "Oracle":
  65. IDataBase = new Oracle();
  66. break;
  67. case "Access":
  68. IDataBase = new Access();
  69. break;
  70. }
  71. }
  72. }

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

  1. class BadCode
  2. {
  3. string GetBadCode(string DataBaseType)
  4. {
  5. string Result = "";
  6. if (DataBaseType == "Oracle")
  7. {
  8. /*此处大量代码(数十行)运算获得从oracle数据库中获取的结果Result*/
  9. }
  10. else if (DataBaseType == "Access")
  11. {
  12. /*此处大量代码(数十行)运算获得从Access数据库中获取的结果Result*/
  13. }
  14. else if (DataBaseType == "SqlServer")
  15. {
  16. /*此处大量代码(数十行)运算获得从SqlServer数据库中获取的结果Result*/
  17. }
  18. return Result;
  19. }
  20. }

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

  工厂方法模式

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

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

  定义:

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

  类图:

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

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

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

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

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

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

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

  适用范围和适用场景

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

  代码示例

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. IFactory factory = new SqlServerFactory();
  6. IDataBase dataBase = factory.CreateDataBase();
  7. dataBase.Insert();
  8. dataBase.Select();
  9. Console.ReadKey();
  10. }
  11. }
  12. public interface IFactory
  13. {
  14. IDataBase CreateDataBase();
  15. }
  16. public interface IDataBase
  17. {
  18. void Select();
  19. void Insert();
  20. }
  21. public class OracleFactory : IFactory
  22. {
  23. public IDataBase CreateDataBase()
  24. {
  25. return new Oracle();
  26. }
  27. }
  28. public class AccessFactory : IFactory
  29. {
  30. public IDataBase CreateDataBase()
  31. {
  32. return new Access();
  33. }
  34. }
  35. public class SqlServerFactory : IFactory
  36. {
  37. public IDataBase CreateDataBase()
  38. {
  39. return new SqlServer();
  40. }
  41. }
  42. public class SqlServer : IDataBase
  43. {
  44. public void Insert()
  45. {
  46. Console.WriteLine("执行sqlServer类的Insert");
  47. }
  48. public void Select()
  49. {
  50. Console.WriteLine("执行sqlServer类的Select");
  51. }
  52. }
  53. public class Oracle : IDataBase
  54. {
  55. public void Insert()
  56. {
  57. Console.WriteLine("执行Oracle类的Insert");
  58. }
  59. public void Select()
  60. {
  61. Console.WriteLine("执行Oracle类的Select");
  62. }
  63. }
  64. public class Access : IDataBase
  65. {
  66. public void Insert()
  67. {
  68. Console.WriteLine("执行Access类的Insert");
  69. }
  70. public void Select()
  71. {
  72. Console.WriteLine("执行Access类的Select");
  73. }
  74. }

  优点:

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

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

  缺点:

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

  抽象工厂模式

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

  定义

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

  类图:

  核心角色为:

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

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

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

  适用范围和适用场景

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

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

  优点:

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

  缺点:

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

  代码示例

  1. /// <summary>
  2. /// 抽象工厂,两条“产品线”,采矿权和探矿权
  3. /// </summary>
  4. public interface IFactory
  5. {
  6. ICKQ CreateCKQ();
  7. ITKQ CreateTKQ();
  8. }
  9. public class XLFactory : IFactory
  10. {
  11. public ICKQ CreateCKQ()
  12. {
  13. return new CKQXL();
  14. }
  15. public ITKQ CreateTKQ()
  16. {
  17. return new TKQXL();
  18. }
  19. }
  20. public class BGFactory : IFactory
  21. {
  22. public ICKQ CreateCKQ()
  23. {
  24. return new CKQBG();
  25. }
  26. public ITKQ CreateTKQ()
  27. {
  28. return new TKQBG();
  29. }
  30. }
  31. /// <summary>
  32. /// 采矿权“产品线”下的“产品”
  33. /// </summary>
  34. public interface ICKQ
  35. {
  36. //签收
  37. void Sign();
  38. //办理完成
  39. void Finish();
  40. //保存
  41. void Save();
  42. }
  43. /// <summary>
  44. /// 探矿权“产品线”下的“产品”
  45. /// </summary>
  46. public interface ITKQ
  47. {
  48. void Sign();
  49. void Finish();
  50. void Save();
  51. }
  52. public class CKQXL : ICKQ
  53. {
  54. public void Finish()
  55. {
  56. Console.WriteLine("实现采矿权新立的办理完成");
  57. }
  58. public void Save()
  59. {
  60. Console.WriteLine("实现采矿权新立的保存");
  61. }
  62. public void Sign()
  63. {
  64. Console.WriteLine("实现采矿权新立的签收");
  65. }
  66. }
  67. public class CKQBG : ICKQ
  68. {
  69. public void Finish()
  70. {
  71. Console.WriteLine("实现采矿权变更的办理完成");
  72. }
  73. public void Save()
  74. {
  75. Console.WriteLine("实现采矿权变更的办理完成");
  76. }
  77. public void Sign()
  78. {
  79. Console.WriteLine("实现采矿权变更的办理完成");
  80. }
  81. }
  82. public class TKQBG: ITKQ
  83. {
  84. public void Finish()
  85. {
  86. Console.WriteLine("实现探矿权变更的办理完成");
  87. }
  88. public void Save()
  89. {
  90. Console.WriteLine("实现探矿权变更的保存");
  91. }
  92. public void Sign()
  93. {
  94. Console.WriteLine("实现探矿权变更的签收");
  95. }
  96. }
  97. public class TKQXL:ITKQ
  98. {
  99. public void Finish()
  100. {
  101. Console.WriteLine("实现探矿权新立的办理完成");
  102. }
  103. public void Save()
  104. {
  105. Console.WriteLine("实现探矿权新立的保存");
  106. }
  107. public void Sign()
  108. {
  109. Console.WriteLine("实现探矿权新立的签收");
  110. }
  111. }
  112. static void Main(string[] args)
  113. {
  114. IFactory factory = new XLFactory();
  115. ICKQ ckq = factory.CreateCKQ();
  116. ckq.Save();
  117. ckq.Sign();
  118. ckq.Finish();
  119. ITKQ tkq = factory.CreateTKQ();
  120. tkq.Save();
  121. tkq.Sign();
  122. tkq.Finish();
  123. Console.ReadKey();
  124. }

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

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

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

代码地址:

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

C#学习之设计模式:工厂模式的更多相关文章

  1. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  2. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  3. Java设计模式学习记录-简单工厂模式、工厂方法模式

    前言 之前介绍了设计模式的原则和分类等概述.今天开启设计模式的学习,首先要介绍的就是工厂模式,在介绍工厂模式前会先介绍一下简单工厂模式,这样由浅入深来介绍. 简单工厂模式 做法:创建一个工厂(方法或类 ...

  4. 学习:java设计模式—工厂模式

    一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类: 1)简单工厂模式(Simple Facto ...

  5. 设计模式学习心得<抽象工厂模式 Abstract Factory>

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  6. [GeekBand] 设计模式——工厂模式学习笔记

     本文参考文献:GeekBand课堂内容,授课老师:李建忠 :大话设计模式 其余的模式方法请自行查看Geekband相关课程,在此不累述. 这周的课题是: 针对DrawingSystem中的基类Sha ...

  7. Java设计模式(学习整理)---工厂模式

    1.工厂模式 1.1 为什么使用工厂模式? 因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时 ...

  8. Java设计模式学习记录-抽象工厂模式

    前言 上篇博客介绍了简单工厂模式和工厂方法模式,这次介绍抽象工厂模式,抽象工厂模式和工厂方法模式的区别在于需要创建对象的复杂程度上. 抽象工厂模式 抽象工厂模式是围绕着一个超级工厂创建其他工厂.这个超 ...

  9. 设计模式——工厂模式(Factory)

    要想正确理解设计模式,首先必须明白它是为了解决什么问题而提出来的. 设计模式学习笔记 --Shulin 转载请注明出处:http://blog.csdn.net/zhshulin 1.概念 工厂模式定 ...

  10. 一张图搞定Java设计模式——工厂模式! 就问你要不要学!

    小编今天分享的内容是Java设计模式之工厂模式. 收藏之前,务必点个赞,这对小编能否在头条继续给大家分享Java的知识很重要,谢谢!文末有投票,你想了解Java的哪一部分内容,请反馈给我. 获取学习资 ...

随机推荐

  1. P1629 邮递员送信

    题目描述: 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员 ...

  2. 51Nod 1090 3个数和为0 set 二分优化

    给出一个长度为N的无序数组,数组中的元素为整数,有正有负包括0,并互不相等.从中找出所有和 = 0的3个数的组合.如果没有这样的组合,输出No Solution.如果有多个,按照3个数中最小的数从小到 ...

  3. 浅谈Android中Serializable和Parcelable使用区别

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 Android开发的时候,我们时长遇到传递对象的需求,但是我们无法将对象的引用传给Activity或者Fragment,我们需要将这些对象 ...

  4. 使用python处理excle表格

    # -*- coding: utf-8 -*- import xlrd ########################### #通用功能,读取excel表格中所有数据 #返回一个包含所有单元格名和对 ...

  5. 简单理解OpenGL模型视图变换

    前几天学习了OpenGL的绘图原理(其实就是坐标的不停变换变换),看到网上有个比较好的例程,于是学习了下,并在自己感兴趣的部分做了注释. 首先通过glMatrixMode(GL_MODELVIEW)设 ...

  6. 豌豆夹Redis解决方式Codis源代码剖析:Proxy代理

    豌豆夹Redis解决方式Codis源代码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描写叙述: Codis is a proxy b ...

  7. php&amp;&amp;页面静态化

    页面静态化.主要是出于两个方面的考虑.     第一:訪问html页面的速度比訪问php页面的速度快.在訪问php页面时候.须要对php进行解析.訪问html时候,直接浏览器能够解析出来.特别是PV量 ...

  8. 前端优化之动画为什么要尽量用css3代替js

    导致JavaScript效率低的两大原因:操作DOM和使用页面动画.通常我们会通过频繁的操作 DOM的CSS来实现视觉上的动画效果,导致js效率低的两个因素都包括在内了在频繁的操作DOM和CSS时,浏 ...

  9. jquery技巧小结

    由于主要还是负责后端,所以前端很多东西都不熟悉,jQuery作为web开发必备技能,有很多知识点,老是记不清楚,所以在这边整理一下. 1.加载页面后执行 $(function(){ //程序段 }) ...

  10. springboot(十三):springboot小技巧

    一些springboot小技巧.小知识点 初始化数据 我们在做测试的时候经常需要初始化导入一些数据,如何来处理呢?会有两种选择,一种是使用Jpa,另外一种是Spring JDBC.两种方式各有区别下面 ...