1、引言
在上一篇文章中咱们讲解了过渡的一种模式叫作【简单工厂】,也有叫【静态工厂】的,经过对简单工厂模式得了解,咱们也发现了它的缺点,就是随着需求的变化咱们要不停地修改工厂里面的方法的代码,需求变化越多,里面的If--Else--也越多,这样就会形成简单工厂的实现逻辑过于复杂。设计模式是遵循必定原则而得来的,好比,咱们要怎么增长代码,怎么修改代码,不是想怎么来就怎么来的,其中一个原则就是OCP原则,中文是【开放关闭原则】,对增长代码开发,对修改代码关闭,因此咱们就不能老是这样修改简单工厂里面的方法。本章介绍的工厂方法模式能够解决简单工厂模式中存在的这个问题,下面就具体看看工厂方法模式是如何解决该问题的。编程
2、工厂方法模式的胡介绍
2.一、动机(Motivate)
在软件系统的构建过程当中,常常面临着“某个对象”的建立工做:因为需求的变化,这个对象(的具体实现)常常面临着剧烈的变化,可是它却拥有比较稳定的接口。设计模式
如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其余依赖对象的对象”不随着需求改变而改变?
2.二、意图(Intent)
定义一个用于建立对象的接口,让子类决定实例化哪个类。Factory Method使得一个类的实例化延迟到子类。 --《设计模式》GoF
2.三、结构图(Structure)
2.四、模式的组成
能够看出,在工厂方法模式的结构图有如下角色:
(1)、抽象工厂角色(Creator): 充当抽象工厂角色,定义工厂类所具备的基本的操做,任何具体工厂都必须继承该抽象类。
(2)、具体工厂角色(ConcreteCreator):充当具体工厂角色,该类必须继承抽象工厂角色,实现抽象工厂定义的方法,用来建立具体产品。
(3)、抽象产品角色(Product):充当抽象产品角色,定义了产品类型全部具备的基本操做,具体产品必须继承该抽象类。
(4)、具体产品角色(ConcreteProduct):充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类建立,它们之间有一一对应的关系。
2.五、工厂方法模式的代码实现
【简单工厂模式】的问题是:若是有新的需求就须要修改工厂类里面建立产品对象实例的那个方法的实现代码,在面向对象设计一个原则就是哪里有变化,我就封装哪里。还有另外两个大的原则,其一是:面向抽象编程,细节和高层实现都要依赖抽象,第二个原则是:多组合,少继承。这三个原则是最根本的原则,学习设计模式必须以这三个原则为基点,不然都是枉然。根据这三大原则又衍生出来6个具体的原则,分别是【单一职责原则】,【里氏替换原则】,【依赖倒置原则】,【接口隔离原则】、【迪米特法则】和【开闭原则】,既然工厂类有变化,咱们就封装它,面向抽象编程,咱们先抽象出一个工厂基类,而后,每一个需求就实现一个具体的工厂类,这样咱们就符合了【开闭原则OCP】,让一个工厂生产一款产品,并一一对应。咱们把具体产品的建立推迟到子类中,此时工厂类(这类是基类了)再也不负责全部产品的建立,而只是给出具体工厂必须实现的接口,这样工厂方法模式就能够容许系统不修改工厂类逻辑的状况下来添加新产品,这样也就克服了简单工厂模式中缺点。下面就是工厂方法模式的实现代码:ide
2 namespace 设计模式之工厂方法模式 3 { 4 /// <summary> 5 /// 汽车抽象类 6 /// </summary> 7 public abstract class Car 8 { 9 // 开始行驶 10 public abstract void Go(); 11 } 12 13 /// <summary> 14 /// 红旗汽车 15 /// </summary> 16 public class HongQiCar : Car 17 { 18 public override void Go() 19 { 20 Console.WriteLine("红旗汽车开始行驶了!"); 21 } 22 } 23 24 /// <summary> 25 /// 奥迪汽车 26 /// </summary> 27 public class AoDiCar : Car 28 { 29 public override void Go() 30 { 31 Console.WriteLine("奥迪汽车开始行驶了"); 32 } 33 } 34 35 /// <summary> 36 /// 抽象工厂类 37 /// </summary> 38 public abstract class Factory 39 { 40 // 工厂方法 41 public abstract Car CreateCar(); 42 } 43 44 /// <summary> 45 /// 红旗汽车工厂类 46 /// </summary> 47 public class HongQiCarFactory:Factory 48 { 49 /// <summary> 50 /// 负责生产红旗汽车 51 /// </summary> 52 /// <returns></returns> 53 public override Car CreateCar() 54 { 55 return new HongQiCar(); 56 } 57 } 58 59 /// <summary> 60 /// 奥迪汽车工厂类 61 /// </summary> 62 public class AoDiCarFactory:Factory 63 { 64 /// <summary> 65 /// 负责建立奥迪汽车 66 /// </summary> 67 /// <returns></returns> 68 public override Car CreateCar() 69 { 70 return new AoDiCar(); 71 } 72 } 73 74 /// <summary> 75 /// 客户端调用 76 /// </summary> 77 class Client 78 { 79 static void Main(string[] args) 80 { 81 // 初始化建立汽车的两个工厂 82 Factory hongQiCarFactory = new HongQiCarFactory(); 83 Factory aoDiCarFactory = new AoDiCarFactory(); 84 85 // 生产一辆红旗汽车 86 Car hongQi = hongQiCarFactory.CreateCar(); 87 hongQi.Go(); 88 89 //生产一辆奥迪汽车 90 Car aoDi = aoDiCarFactory.CreateCar(); 91 aoDi.Go(); 92 93 Console.Read(); 94 } 95 } 96 }
在【工厂方法模式】中,咱们一样也把汽车的类抽象出来一个抽象的基类,这里正好符合了【面向抽象编程】,客户端在使用的时候不会依赖具体的什么汽车。使用工厂方法实现的系统,若是系统须要添加新产品时,咱们能够利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不须要作任何改动。例如,咱们想增长一辆奔驰车,咱们只需从Car抽象类下继承一个BenChiCar类,同时在从Factory抽象基类下继承一个“奔驰”的工厂类BenChinaCarFactory就能够了,这样扩展符合OCP的原则。具体代码为:学习
1 /// <summary> 2 /// 奔驰车 3 /// </summary> 4 public class BenChiCar : Car 5 { 6 /// <summary> 7 /// 重写抽象类中的方法 8 /// </summary> 9 public override void Go() 10 { 11 Console.WriteLine("奔驰车开始行驶了!"); 12 } 13 } 14 15 /// <summary> 16 /// 奔驰车的工厂类 17 /// </summary> 18 public class BenChiCarFactory : Factory 19 { 20 /// <summary> 21 /// 负责生产奔驰车 22 /// </summary> 23 /// <returns></returns> 24 public override Car CreateCar() 25 { 26 return new BenChiCar(); 27 } 28 } 29 30 /// <summary> 31 /// 客户端调用 32 /// </summary> 33 class Client 34 { 35 static void Main(string[] args) 36 { 37 38 // 若是客户又生产一辆奔驰车 39 // 再另外初始化一个奔驰车的工厂 40 Factory benChiCarFactory = new BenChiCarFactory(); 41 42 // 利用奔驰车的工厂生产奔驰车 43 Car benChi = benChiCarFactory.CreateCar(); 44 benChi.Go(); 45 46 Console.Read(); 47 } 48 } 49
3、Factory Method模式的几个要点ui
Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个常常变化的具体类型,紧耦合关系会致使软件的脆弱。spa
Factory Method模式经过面向对象的手法,将所要建立的具体对象工做延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
.net
Factory Method模式解决“单个对象”的需求变化;设计
AbstractFactory模式解决“系列对象”的需求变化;code
Builder模式解决“对象部分”的需求变化;
3.1】、工厂方法模式的优势:
(1)、 在工厂方法中,用户只须要知道所要产品的具体工厂,无须关系具体的建立过程,甚至不须要具体产品类的类名。
(2)、在系统增长新的产品时,咱们只须要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。
3.2】、工厂方法模式的缺点:
(1)、每次增长一个产品时,都须要增长一个具体类和对象实现工厂,是的系统中类的个数成倍增长,在必定程度上增长了系统的复杂度,同时也增长了系统具体类的依赖。这并非什么好事。
3.3】、工厂方法模式使用的场景:
(1)、一个类不知道它所须要的对象的类。在工厂方法模式中,咱们不须要具体产品的类名,咱们只须要知道建立它的具体工厂便可。
(2)、一个类经过其子类来指定建立那个对象。在工厂方法模式中,对于抽象工厂类只须要提供一个建立产品的接口,而由其子类来肯定具体要建立的对象,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
(3)、将建立对象的任务委托给多个工厂子类中的某一个,客户端在使用时能够无须关心是哪个工厂子类建立产品子类,须要时再动态指定。
4、.NET中实现了工厂方法的类
.NET 类库中也有不少实现了工厂方法的类,例如Asp.net中,处理程序对象是具体用来处理请求,当咱们请求一个*.aspx的文件时,此时会映射到System.Web.UI.PageHandlerFactory类上进行处理,而对*.ashx的请求将映射到System.Web.UI.SimpleHandlerFactory类中(这两个类都是继承于IHttpHandlerFactory接口的),关于这点说明咱们能够在“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Web.Config”文件中找到相关定义,具体定义以下:对象
<add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True"/> <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/> <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True"/>
配置文件截图了一部分,有时间你们能够本身去研究一下。
下面咱们就具体看下工厂方法模式在Asp.net中是如何实现的,若是对一个Index.aspx页面发出请求时,将会调用PageHandlerFactory中GetHandler方法来建立一个Index.aspx对象,它们之间的类图关系以下:
5、总结
每种模式都有本身的使用场合,切记,若是使用错误,还不如不用。工厂方法模式经过面向对象编程中的多态性来将对象的建立延迟到具体工厂中,从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。
学习设计模式咱们必定要谨记设计模式的几大原则,不然是徒劳无功的。就像学务工同样,咱们要记心法。6大原则就像孤独求败的独孤九剑的剑诀,学会了,变化多端。