一、引言
在上一篇文章中我们讲解了过渡的一种模式叫做【简单工厂】,也有叫【静态工厂】的,通过对简单工厂模式得了解,我们也发现了它的缺点,就是随着需求的变化我们要不停地修改工厂里面的方法的代码,需求变化越多,里面的If--Else--也越多,这样就会造成简单工厂的实现逻辑过于复杂。设计模式是遵循一定原则而得来的,比如,我们要怎么增加代码,怎么修改代码,不是想怎么来就怎么来的,其中一个原则就是OCP原则,中文是【开放关闭原则】,对增加代码开发,对修改代码关闭,所以我们就不能总是这样修改简单工厂里面的方法。要想解决这个问题,本章介绍的工厂方法模式可以解决简单工厂模式中存在的这个问题,下面就具体看看工厂模式是如何解决该问题的。
二、工厂方法模式的实现
【简单工厂模式】的问题是:如果有新的需求就需要修改工厂类里面创建产品对象实例的那个方法的实现代码,在面向对象设计一个原则就是哪里有变化,我就封装哪里。还有另外两个大的原则,其一是:面向抽象编程,细节和高层实现都要依赖抽象,第二个原则是:多组合,少继承。这三个原则是最根本的原则,学习设计模式必须以这三个原则为基点,否则都是枉然。根据这三大原则又衍生出来6个具体的原则,分别是【单一职责原则】,【里氏替换原则】,【依赖倒置原则】,【接口隔离原则】、【迪米特法则】和【开闭原则】,既然工厂类有变化,我们就封装它,面向抽象编程,我们先抽象出一个工厂基类,然后,每个需求就实现一个具体的工厂类,这样我们就符合了【开闭原则OCP】,让一个工厂生产一款产品,并一一对应。我们把具体产品的创建推迟到子类中,此时工厂类(这类是基类了)不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点。下面看下工厂模式的具体实现代码(这里还是以简单工厂模式中点菜的例子来实现):
- namespace 设计模式之工厂方法模式
- {
- /// <summary>
- /// 菜抽象类
- /// </summary>
- public abstract class Food
- {
- // 输出点了什么菜
- public abstract void Print();
- }
- /// <summary>
- /// 西红柿炒鸡蛋这道菜
- /// </summary>
- public class TomatoScrambledEggs : Food
- {
- public override void Print()
- {
- Console.WriteLine("西红柿炒蛋好了!");
- }
- }
- /// <summary>
- /// 土豆肉丝这道菜
- /// </summary>
- public class ShreddedPorkWithPotatoes : Food
- {
- public override void Print()
- {
- Console.WriteLine("土豆肉丝好了");
- }
- }
- /// <summary>
- /// 抽象工厂类
- /// </summary>
- public abstract class Factory
- {
- // 工厂方法
- public abstract Food CreateFoddFactory();
- }
- /// <summary>
- /// 西红柿炒蛋工厂类
- /// </summary>
- public class TomatoScrambledEggsFactory:Factory
- {
- /// <summary>
- /// 负责创建西红柿炒蛋这道菜
- /// </summary>
- /// <returns></returns>
- public override Food CreateFoddFactory()
- {
- return new TomatoScrambledEggs();
- }
- }
- /// <summary>
- /// 土豆肉丝工厂类
- /// </summary>
- public class ShreddedPorkWithPotatoesFactory:Factory
- {
- /// <summary>
- /// 负责创建土豆肉丝这道菜
- /// </summary>
- /// <returns></returns>
- public override Food CreateFoddFactory()
- {
- return new ShreddedPorkWithPotatoes();
- }
- }
- /// <summary>
- /// 客户端调用
- /// </summary>
- class Client
- {
- static void Main(string[] args)
- {
- // 初始化做菜的两个工厂()
- Creator shreddedPorkWithPotatoesFactory = new ShreddedPorkWithPotatoesFactory();
- Creator tomatoScrambledEggsFactory = new TomatoScrambledEggsFactory();
- // 开始做西红柿炒蛋
- Food tomatoScrambleEggs = tomatoScrambledEggsFactory.CreateFoddFactory();
- tomatoScrambleEggs.Print();
- //开始做土豆肉丝
- Food shreddedPorkWithPotatoes = shreddedPorkWithPotatoesFactory.CreateFoddFactory();
- shreddedPorkWithPotatoes.Print();
- Console.Read();
- }
- }
- }
在【工厂方法模式】中,我们同样也把食物的类抽象出来一个抽象的基类,这里正好符合了【面向抽象编程】,客户端在使用的时候不会依赖具体的食物。使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任何改动。例如,我们想吃肉末茄子,我们只需从Food抽象类下继承一个MincedMeatEggplant类,同时在从Factory抽象基类下继承一个“肉末茄子”的工厂类MincedMeatEggplantFactory就可以了,这样扩展符合OCP的原则。具体代码为:
- /// <summary>
- /// 肉末茄子这道菜
- /// </summary>
- public class MincedMeatEggplant : Food
- {
- /// <summary>
- /// 重写抽象类中的方法
- /// </summary>
- public override void Print()
- {
- Console.WriteLine("肉末茄子好了");
- }
- }
- /// <summary>
- /// 肉末茄子工厂类,负责创建肉末茄子这道菜
- /// </summary>
- public class MincedMeatEggplantFactory : Factory
- {
- /// <summary>
- /// 负责创建肉末茄子这道菜
- /// </summary>
- /// <returns></returns>
- public override Food CreateFoddFactory()
- {
- return new MincedMeatEggplant();
- }
- }
- /// <summary>
- /// 客户端调用
- /// </summary>
- class Client
- {
- static void Main(string[] args)
- {
- // 如果客户又想点肉末茄子了
- // 再另外初始化一个肉末茄子工厂
- Creator minceMeatEggplantFactor = new MincedMeatEggplantFactory();
- // 利用肉末茄子工厂来创建肉末茄子这道菜
- Food minceMeatEggplant = minceMeatEggplantFactor.CreateFoddFactory();
- minceMeatEggplant.Print();
- Console.Read();
- }
- }
三、工厂方法模式的UML图
工厂方法模式讲完了,让我们看下工厂模式中各类之间的UML图,我们的印象会更深刻:
从UML图可以看出,在工厂方法模式中,工厂类与具体产品类具有平行的等级结构,它们之间是一一对应的。针对UML图的解释如下:
Creator类:充当抽象工厂角色,任何具体工厂都必须继承该抽象类
TomatoScrambledEggsFactory和ShreddedPorkWithPotatoesFactory类:充当具体工厂角色,用来创建具体产品
Food类:充当抽象产品角色,具体产品的抽象类。任何具体产品都应该继承该类
TomatoScrambledEggs和ShreddedPorkWithPotatoes类:充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
四、.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对象,它们之间的类图关系如下:
五、总结
每种模式都有自己的使用场合,切记,如果使用错误,还不如不用。工厂方法模式通过面向对象编程中的多态性来将对象的创建延迟到具体工厂中,从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。
学习设计模式我们一定要谨记设计模式的几大原则,否则是徒劳无功的。就像学务工一样,我们要记心法。6大原则就像孤独求败的独孤九剑的剑诀,学会了,变化无穷。