无废话C#设计模式之四:Factory Method
意图
定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
场景
上次,我们使用抽象工厂解决了生产一组产品的问题,但是我们把各个场景作为了具体工厂来生产场景模式和场景纹理两个产品。在调用代码中也并没有出现具体工厂的影子。其实,场景类要做的不仅仅是创建具体的产品系列,可能它还需要做一个初始化工作。那么,我们就需要在调用代码中能得到这个场景类。
在前一节中,由于场景类(比如HalfPaper)本身是具体级别的(具体工厂)。那么,我们也不应该在调用代码中直接依赖场景类。因此,我们可以使用工厂方法来生产这个具体产品。
示例代码
using System; using System.Reflection; namespace FactoryMethodExample { classProgram { staticvoid Main(string[] args) { Patrix patrix = newPatrix(); patrix.LoadScene("HalfPaper"); patrix.LoadScene("Matrix"); } } classPatrix { privatePatrixSceneFactory GetGameScene(string gameSceneName) { return (PatrixSceneFactory)Assembly.Load("FactoryMethodExample").CreateInstance("FactoryMethodExample." + gameSceneName + "Factory"); } publicvoid LoadScene(string gameSceneName) { PatrixSceneFactory psf = GetGameScene(gameSceneName); PatrixScene ps = psf.CreateScene(); ps.InitScene(); } } abstractclassPatrixSceneFactory { publicabstractPatrixScene CreateScene(); } abstractclassPatrixScene { publicvoid InitScene() { Texture texture = CreateTexture(); Model model = CreateModel(); model.FillTexture(texture); } publicabstractModel CreateModel(); publicabstractTexture CreateTexture(); } abstractclassModel { publicabstractvoid FillTexture(Texture texture); } abstractclassTexture { } classHalfPaperFactory : PatrixSceneFactory { publicoverridePatrixScene CreateScene() { returnnewHalfPaper(); } } classHalfPaper : PatrixScene { public HalfPaper() { Console.WriteLine("HalfPaper Creating"); } publicoverrideModel CreateModel() { returnnewHalfPaperModel(); } publicoverrideTexture CreateTexture() { returnnewHalfPaperTexture(); } } classHalfPaperModel : Model { public HalfPaperModel() { Console.WriteLine("HalfPaper Model Creating"); } publicoverridevoid FillTexture(Texture texture) { Console.WriteLine("HalfPaper Model is filled Texture"); } } classHalfPaperTexture : Texture { public HalfPaperTexture() { Console.WriteLine("HalfPaper Texture Created"); } } classMatrixFactory : PatrixSceneFactory { publicoverridePatrixScene CreateScene() { returnnewMatrix(); } } classMatrix : PatrixScene { public Matrix() { Console.WriteLine("Matrix Created"); } publicoverrideModel CreateModel() { returnnewMatrixModel(); } publicoverrideTexture CreateTexture() { returnnewMatrixTexture(); } } classMatrixModel : Model { public MatrixModel() { Console.WriteLine("Matrix Model Created"); } publicoverridevoid FillTexture(Texture texture) { Console.WriteLine("Matrix Model is filled Texture"); } } classMatrixTexture : Texture { public MatrixTexture() { Console.WriteLine("Matrix Texture Created"); } } } |
代码执行结果如下图:
代码说明
l 这个代码基于前一节抽象工厂的代码修改而来,因此代码比较厂。其中能体现的设计模式有抽象工厂、工厂方法以及模版方法。
l 这次的PatrixSceneFactory和前一节的不同,它是一个产品的抽象工厂,也就是工厂方法模式中的抽象工厂角色,它是具体产品工厂的抽象形式。
l HalfPaperFactory和MatrixFactory是工厂方法模式中的具体工厂角色,它负责创建具体的产品。
l PatrixScene是工厂方法模式中的抽象产品角色,同时也是抽象工厂模式中的抽象工厂角色。它既是场景的抽象形式,又负责创建每个场景中的产品系列,也就是模型和纹理。还有,它的InistScene()方法还体现了模版方法的思想,封装了产品初始化过程中的共同步骤。
l HalfPaper和Matrix当然就是工厂方法模式中的具体产品角色了,同时,它们也是抽象工厂模式中的具体工厂角色。
l 从这个例子可以看出,抽象工厂针对一组产品的创建进行抽象,抽象程度比较高。抽象工厂生产重点在于规范一组产品的创建,能让产品线保持产品的一致。比如,N卡不管是7系列还是8系列,总会分低端的7300,8300和中端的7600,8600以及高端的7900,8900。
l 而工厂方法针对某种产品的创建,每种产品在创建的过程中可能会有一些相似的步骤,那么就可以在抽象产品中进行一些提取,自然而然运用到了模版方法。工厂方法还能针对具体产品创建时的易变性,在这里我们可能很清楚HalfPaperFactory一定会创建HalfPaper这个产品,但是万一以后改为创建HalfPaperSpecial了呢?有了工厂方法,我们可以只需修改HalfPaperFactory就可以了。
l 不管怎么样,目的还是让调用方尽量和接口依赖(或者说和稳定的东西去依赖,让变化在接口下面变),既是要以来具体类型,也希望能只依赖一个。试想一下,如果没有抽象工厂和工厂方法,也少了这些抽象类型,那么调用方可能就要依赖具体场景类型和具体的纹理以及模型类型。并且在调用的时候,通过条件来判断并且创建各种具体类型,一旦有新的场景需要实现,调用方代码可能就需要做很大的调整。暂且不说调整的工作量有多少,调整所带来的风险谁能承担呢?
何时采用
l 从代码角度来说, 如果我们需要创建一个易变的对象,或是希望对象由子类决定创建哪个对象的时候可以考虑工厂方法。
l 从应用角度来说, 如果我们觉得具体产品的创建不稳定,或者客户端根本无需知道创建哪个具体产品的时候可以使用工厂方法。后者对于框架和工具包软件来说更常见,比如有一个打印类负责打印图纸,我们需要得到一个打印对象,对于调用方来说并不知道要使用超宽打印对象还是普通打印对象,我们可以通过工厂方法使客户端和抽象打印工厂直接沟通,由它来决定具体创建哪个打印对象。
实现要点
l 通过继承创建具体产品。很多时候,每一种具体产品对应一个具体的工厂来创建。
l 使用具体工厂类来决定怎么样创建具体产品。调用方并不关心工厂创建的是哪个游戏场景,它只用知道工厂给我的是一个游戏场景即可。
注意事项
l 工厂方法通常需要为每个具体产品对应一个具体工厂,如果滥用的话会使得类的数目急剧增多。