C#设计模式学习笔记:(2)工厂方法模式
一、引言
接上一篇C#设计模式学习笔记:简单工厂模式(工厂方法模式前奏篇),通过简单工厂模式的了解,它的缺点就是随着需求的变化我们要不停地修改工厂里面的方法的代码,需求变化越多,里面的if--else也越多,这样就会造成简单工厂的实现逻辑过于复杂。
依设计原则里的开闭原则--对增加代码开放,对修改代码关闭,我们不能总是这样修改简单工厂里面的方法。
下面看看工厂方法模式是如何解决该问题的?
二、工厂方法模式介绍
工厂方法模式:英文名称--Factory Method Pattern;分类--创建型。
2.1、动机(Motivate)
在软件系统的构建过程中,经常面临着“某个对象”的创建工作:由于需求的变化,这个对象(的具体实现)经常面临着剧烈的变化,但是它却拥有比较稳定的接口。
如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖对象的对象”不随着需求改变而改变?
2.2、意图(Intent)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。--《设计模式》GoF
2.3、结构图(Structure)
2.4、模式的组成
从上图可以看出,在工厂方法模式的结构图有以下角色:
1)抽象工厂角色(Creator):充当抽象工厂角色,定义工厂类所具有的基本的操作,任何具体工厂都必须继承该抽象类。
2)具体工厂角色(ConcreteCreator):充当具体工厂角色,该类必须继承抽象工厂角色,实现抽象工厂定义的方法,用来创建具体产品。
3)抽象产品角色(Product):充当抽象产品角色,定义了产品类型所有具有的基本操作,具体产品必须继承该抽象类。
4)具体产品角色(ConcreteProduct):充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
2.5、工厂方法模式的具体实现
面向对象设计三大原则:
1)哪里有变化就封装哪里。
2)面向抽象编程,细节和高层实现都要依赖抽象。
3)多组合,少继承。
这三大原则是最根本的原则,学习设计模式必须以这三个原则为基点,否则都是枉然。根据这三大原则又衍生出来6个具体的原则,分别是单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则。
既然工厂类有变化,我们就封装它,面向抽象编程。我们先抽象出一个工厂基类,然后每个需求都实现一个具体的工厂类,这样我们就符合了开闭原则,让一个工厂生产一款产品,并一一对应。具体产品的创建推迟到子类中,此时工厂类(基类)不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,也就克服了简单工厂模式中缺点。
下面是工厂方法模式的实现代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { /// <summary> /// 汽车抽象类 /// </summary> public abstract class Car { //开始行驶 public abstract void Go(); } /// <summary> /// 红旗汽车 /// </summary> public class HongQiCar : Car { public override void Go() { Console.WriteLine("红旗汽车生产中。"); } } /// <summary> /// 奥迪汽车 /// </summary> public class AoDiCar : Car { public override void Go() { Console.WriteLine("奥迪汽车生产中。"); } } /// <summary> /// 抽象工厂类 /// </summary> public abstract class Factory { //工厂方法 public abstract Car CreateCar(); } /// <summary> /// 红旗汽车工厂类 /// </summary> public class HongQiCarFactory : Factory { /// <summary> /// 负责生产红旗汽车 /// </summary> /// <returns></returns> public override Car CreateCar() { return new HongQiCar(); } } /// <summary> /// 奥迪汽车工厂类 /// </summary> public class AoDiCarFactory : Factory { /// <summary> /// 负责创建奥迪汽车 /// </summary> /// <returns></returns> public override Car CreateCar() { return new AoDiCar(); } } static void Main(string[] args) { #region 工厂方法模式 //初始化创建汽车的两个工厂 Factory hongQiCarFactory = new HongQiCarFactory(); Factory aoDiCarFactory = new AoDiCarFactory(); //生产一辆红旗汽车 Car hongQi = hongQiCarFactory.CreateCar(); hongQi.Go(); //生产一辆奥迪汽车 Car aoDi = aoDiCarFactory.CreateCar(); aoDi.Go(); Console.Read(); #endregion } }
运行结果如下:
使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任何改动。假如我们想生产奔驰车,我们只需从Car抽象类下继承一个BenChiCar类、在Factory抽象类下继承一个“奔驰”的工厂类BenChiCarFactory就可以实现了:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Program { /// <summary> /// 汽车抽象类 /// </summary> public abstract class Car { //开始行驶 public abstract void Go(); } /// <summary> /// 红旗汽车 /// </summary> public class HongQiCar : Car { public override void Go() { Console.WriteLine("红旗汽车生产中。"); } } /// <summary> /// 奥迪汽车 /// </summary> public class AoDiCar : Car { public override void Go() { Console.WriteLine("奥迪汽车生产中。"); } } /// <summary> /// 奔驰汽车 /// </summary> public class BenChiCar : Car { public override void Go() { Console.WriteLine("奔驰汽车生产中。"); } } /// <summary> /// 抽象工厂类 /// </summary> public abstract class Factory { //工厂方法 public abstract Car CreateCar(); } /// <summary> /// 红旗汽车工厂类 /// </summary> public class HongQiCarFactory : Factory { /// <summary> /// 负责生产红旗汽车 /// </summary> /// <returns></returns> public override Car CreateCar() { return new HongQiCar(); } } /// <summary> /// 奥迪汽车工厂类 /// </summary> public class AoDiCarFactory : Factory { /// <summary> /// 负责创建奥迪汽车 /// </summary> /// <returns></returns> public override Car CreateCar() { return new AoDiCar(); } } /// <summary> /// 奔驰汽车工厂类 /// </summary> public class BenChiCarFactory : Factory { /// <summary> /// 负责生产奔驰汽车 /// </summary> /// <returns></returns> public override Car CreateCar() { return new BenChiCar(); } } static void Main(string[] args) { #region 工厂方法模式 //初始化创建汽车的两个工厂 Factory hongQiCarFactory = new HongQiCarFactory(); Factory aoDiCarFactory = new AoDiCarFactory(); Factory benChiCarFactory = new BenChiCarFactory(); //生产一辆红旗汽车 Car hongQi = hongQiCarFactory.CreateCar(); hongQi.Go(); //生产一辆奥迪汽车 Car aoDi = aoDiCarFactory.CreateCar(); aoDi.Go(); //生产一辆奔驰汽车 Car benChi = benChiCarFactory.CreateCar(); benChi.Go(); Console.Read(); #endregion } }
运行结果如下:
三、Factory Method模式的几个要点
• Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱;
• Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系;
• Factory Method模式解决“单个对象”的需求变化;
• AbstractFactory模式解决“系列对象”的需求变化;
• Builder模式解决“对象部分”的需求变化;
3.1、工厂方法模式的优点
1)在工厂方法中,用户只需要知道所要产品的具体工厂,无须关心具体的创建过程,甚至不需要具体产品类的类名。
2)在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。
3.2、工厂方法模式的缺点
1)每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖,这并不是什么好事。
3.3、工厂方法模式的使用场合
1)一个类不知道它所需要的对象的类。在工厂方法模式中,我们不需要具体产品的类名,我们只需要知道创建它的具体工厂即可。
2)一个类通过其子类来指定创建那个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,由其子类来确定具体要创建的对象。在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。
四、.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对象,它们之间的类图关系如下:
五、总结
每种模式都有自己的使用场合,切记,如果使用错误,还不如不用。工厂方法模式通过面向对象编程中的多态性来将对象的创建延迟到具体工厂中,从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。
学习设计模式我们一定要谨记设计模式的几大原则,否则是徒劳无功的。就像学务工一样,我们要记心法。几大原则就像独孤九剑的剑诀,学会了,变化无穷。