一、模式概述
也许Factory Method模式是设计模式中应用最广泛的模式。在面向对象的设计中,关于对象的管理是其核心所在,而其中对象的创建则是对象管理的第一步。对象的创建非常简单,在C#中,只需要应用new操作符调用对象的构造函数即可,然而创建对象的时机却非常重要。
首先我们从对象的特征来看,代表抽象关系的类型,如接口和抽象类,是不能创建的,换句话说,我们要创建的对象都是与具体的对象类型有关。因此,对象的创建工作必然涉及到设计中的实现细节,从而导致创建者与具体的被创建者之间耦合度增强。举例来说,如果在一个项目中我们需要创建一些图形对象,例如Circle、Square。这些对象的结构如下:
这个结构是非常符合OO思想的,它通过IShape接口将Square和Circle对象抽象出来,根据多态的原理,我们完全可以在程序中用IShape来代替具体的Square和Circle类,从而将具体的对象类型绑定留到运行时。然而,上文说到,接口对象是不能创建的,因此,项目一旦要创建IShape类型的对象,必然要针对具体的对象Square或Circle进行创建操作。例如:
IShape shape = new Square();
如果是开发一个图形工具,诸如Square和Circle之类的对象,其创建工作必然非常频繁。可以设想,在这个项目的各个模块中,将会大量充斥着如上的代码行,导致的结果是各个模块无法与Square对象结耦,这意味着,如果我们改变创建的对象为Circle,就需要修改所有调用new Square()操作的模块。这既加大了工作量,同时也导致了项目的不可扩展性,以及模块的不可重用性。而对于图形对象的抽象IShape来说,也是不必要而失败的。
在面向对象的设计中,我们常常将可能变化的操作进行封装,封装的内容可能仅是某种行为,也可能是一种状态,或者是某些职责。而在当前的案例中,我们需要将对象的创建行为进行封装,这就引入了Factory Method模式。此时的对象就是Factory要生产的产品。既然产品有两种,相对应的工厂也应该是两个,即SquareFactory和CircleFactory。在Factory Method模式中,工厂对象的结构应与产品的结构平行,并与之一一对应,所以,对这两个工厂而言,还需要为其抽象出一个共同的工厂接口IShapeFactory:
代码如下:
public interface IShapeFactory
{
IShape CreateShape();
}
public class SquareFactory:IShapeFactory
{
public IShape CreateShape()
{
return new Square();
}
}
public class CircleFactory:IShapeFactory
{
public IShape CreateShape()
{
return new Circle();
}
}
通过Factory Method模式,我们完成了对象创建的封装,将前面诸如IShape shape = new Square()的代码全部移到了各自的工厂对象中,并放到CreateShape()方法中实现。整个结构如下图所示:
请注意CreateShape()方法的返回类型是IShape类型,这就有效地避免了工厂对象与具体产品对象的依赖。
也许会有人认为,虽然通过工厂方法,将创建IShape对象的职责转交给工厂对象,然而在工厂类的结构中,仍然存在具体的工厂类对象。如此以来,虽然我们解除了模块与具体Shape对象的依赖,却增加了对具体工厂对象的依赖,这会带来何种益处?
让我们从对象创建的频率来分析。对于一个图形工具而言,IShape对象的创建无疑是频繁的,最大的可能性是在这个项目的各个模块中都可能存在创建IShape对象的需要。而工厂对象则不尽然,我们完全可以集中在一个模块中,初始化这个工厂对象,而在需要IShape对象的时候,直接调用工厂实例的CreateShape()就可以达到目的。
举例来说,假设在图形工具中,有三个模块:ModuleA,ModuleB,ModuleC;这三个模块中都需要创建Square对象,则按照原来的设计方案,这三个模块都包含这样一行代码:
IShape shape = new Square();
此时,与Square对象有依赖关系的就包括了ModuleA,ModuleB,ModuleC三个模块。如果我们需要修改shape对象为Circle类型,则这个变动无疑会影响到上述的三个模块。现在,我们引入Factory Method模式,并增加一个模块名为ModuleFactory,在这个模块中,我们创建一个工厂对象:
IShapeFactory shapeFactory = new SquareFactory();
如此以来,原来的三个模块有关Square对象的创建,就相应地修改为:
IShape shape = shapeFactory.CreateShape();
此时,即使需求发生改变,需要对shape对象进行修改,那么我们只需要修改ModuleFactory模块中的代码:
IShapeFactory shapeFactory = new CircleFactory();
而ModuleA,ModuleB,ModuleC三个模块则根本不需要作任何改变。如此的设计改进,虽然在项目中增加了三个工厂对象,并引入了ModuleFactory,但它却完成了ModuleA,ModuleB,ModuleC与具体的Square对象的解耦,从而将这三个模块与产品对象的依赖性转嫁到ModuleFactory上。如此以来,牵一发而不动其全身,极大地提高了模块的重用性。
从上述的分析可知,引入工厂对象并不是简单地为产品建立相应的工厂,而是要注意划分各个模块的职责,将工厂对象的创建放到合适的地方。最佳方案莫过于将创建工厂对象的职责集中起来,放到一个模块中;而不是在需要创建产品时,才创建工厂对象。错误的例子是在创建产品时将工厂对象的创建于产品对象的创建放在一起,并分布在各个模块中:
IShapeFactory shapeFactory = new SquareFactory();
IShape shape = shapeFactory.CreateShape();
这样的做法,则引入Factory Method模式,无异于画蛇添足了。
No comments:
Post a Comment