1. 何为工厂方法模式
工厂方法也称为虚构造器(virtual constructor)。它适用于这种情况: 一个类无法预期需要生成那个类的对象,想让其子类来指定所生成的对象。
工厂方法模式的静态类结构如下图1-1所示。
抽象的Product(产品)定义了工厂方法创建的对象的接口。ConcreteProduct实现了Product接口。Creator定义了返回Product对象的工厂方法。它也可以为工厂方法定义一个默认实现,返回默认ConcreteProduct对象。Creator的其他操作可以调用此工厂方法创建Product对象。ConcreateCreator是Creator的字类,它重载了工厂方法,返回ConcreteProduct的实例。
*定义:
工厂方法模式: 定义创建对象的接口,让子类决定实例化那一个字类。工厂方法使得一个类的实例化延迟到其子类。
2. 工厂方法模式使用举例
根据工厂方法模式的定义和结构图,现在将简单工厂模式中的示例,用工厂方法模式来实现,工厂方法模式实现的结构图如下图2-1所示:
从图中可以看到,在简单工厂模式中,由工厂类(ChartFactory)根据参数负责创建具体的产品(线形图、饼状图);而在工厂方法模式中,工厂类(Factory)只定义了一个创建产品的抽象接口,创建具体产品的工作由具体的工厂(线形图工厂、饼状图工厂)来实现。如果需要增加其他类型的图形绘制,那么使用简单工厂模式实现的话,首先需要增加一个其他图形绘制的类,例如柱状图(BarChart),然后修改工厂类(ChartFactory),在里面加分支语句来判断;使用工厂方法模式实现的话,不仅需要增加图形绘制类,还需要增加具体工厂类(BarFactory)。看到这里,可能大家会感觉到,工厂方法模式不但没有减少难度,反而增加了一些类和复杂度。这样来看,是不是没有必要使用工厂方法模式?咱们再回顾一下开篇介绍的六大设计原则,有一个原则是“开放-关闭原则”,简单工厂模式不仅对扩展开放,而且对修改也开放,违反了“开放-关闭原则”。工厂方法模式是简单工厂模式的进一步抽象,它保持了简单工厂模式的优点(去除了客户端与具体产品的依赖),而且克服了它的缺点(违反开放-关闭原则”)。它的缺点是每增加一个产品,就需要加一个产品工厂的类,增加了额外的开发工作量。
接下来看看代码实现:
(1)IChart.h
@protocol IChart <NSObject>
- (void)drawing;
@end
(2)LineChart
@interface LineChart : NSObject<IChart>
@end
@implementation LineChart
- (void)drawing
{
NSLog(@"LineChart drawing.");
}
@end
(3)PieChart.h
@interface PieChart : NSObject<IChart>
@end
@implementation PieChart
- (void)drawing
{
NSLog(@"PieChart drawing.");
}
@end
(4)Factory.h
@protocol Factory <NSObject>
- (id<IChart>)createChart;
@end
(5)LineFactory
@interface LineFactory : NSObject<Factory>
@end
@implementation LineFactory
- (id<IChart>)createChart
{
return [[LineChart alloc] init];
}
@end
(6)PieFactory
@interface PieFactory : NSObject<Factory>
@end
@implementation PieFactory
- (id<IChart>)createChart
{
return [[PieChart alloc] init];
}
@end
(7)客户端使用
id<Factory> factory = [[LineFactory alloc] init];
// id<Factory> factory = [[PieFactory alloc] init];
id<IChart> chart = [factory createChart];
[chart drawing];
从调用代码可以看出,工厂方法模式从代码中消除了对应用程序特有类的耦合。代码只需处理Product抽象接口(这里是id<IChart>),这样同一代码就可以复用。
从上面的客户端调用代码看到,如果有多处调用绘图的地方,我们需要每处都进行修改,这样的话,实际上也没有达到我们的效果:应对变化,尽可能少的修改代码。下面的方法可以解决此问题。
id<Factory> factory = [[NSClassFromString(@"PieFactory") alloc] init];
id<IChart> chart = [factory createChart];
[chart drawing];
这样的话,我们可以将@"PieFactory"放到配置文件中,当我们需要绘制线形图的时候,只需要修改配置文件即可,客户端的所有代码都不需要改变。
3. 何时使用工厂方法模式
下面三种情景我们会自然而然的想到工厂方法模式:
a. 编译时无法准确预期要创建的对象的类;
b. 类想让其子类决定在运行时创建什么;
c. 类有若干辅助类为其子类,而你想将返回哪个子类这一信息局部化。
使用这一模式的最低限度是,工厂方法能给予类在变更返回哪一种对象这一点上更多的灵活性。使用这一架构的一个常见例子是Cocoa Touch框架中的NSNumber,尽管可以使用常见的 alloc init两步创建NSNumber实例,但这没有什么用,除非使用预先定义的类工厂方法来创建有意义的实例。例如:[NSNumber numberWithBool:YES]
消息会得到NSNumber的子类NSCFBoolean的一个实例,这个实例包含传给类工厂方法的布尔值。