工厂模式是创建型模式,它的作用是创建对象。具体来说,常见的工厂模式包括简单工厂模式,工厂方法模式和抽象工厂模式三种。
简单工厂模式(Simple Factory Pattern)
构成
工厂类
工厂类通常包含一个静态方法(类方法),由该方法根据输入类型负责创建具体的产品(对象)。
抽象产品基类(接口)
它的作用是降低客户端和具体产品之间的耦合度。而且符合了开闭原则,以后需要加入新产品线(对象),客户端调用的代码也基本无需修改。
具体产品类
真正实现业务逻辑的子类。
解决的问题:
工厂模式的核心思想在于:
- 通过引入工厂类,使对象的创建和使用分离了。这样的好处是它们可以独立的变化,易维护和扩展。
- 客户端依赖抽象基类(接口),而不是具体的类,降低了耦合度。
适用的场景:
- 有一组相似的对象,需要集中统一创建时。
- 创建对象的过程较为复杂时。
- 对象很多,并且有扩展需求时。
- 客户端不需要知道创建对象的过程时。
- 客户端使用的对象存在变动的可能,或者根本不知道使用哪一个具体对象时。
举例:
主界面设置一个TextField
和一个button
然后在button
的点击事件中调用工厂方法进行创建不同的类的实例:
id<calculateDelegate> example1 = [SimpleFactory createCalcute:@"+"];
[example1 calculate];
id<calculateDelegate> example2 = [SimpleFactory createCalcute:@"-"];
[example2 calculate];
id<calculateDelegate> example3 = [SimpleFactory createCalcute:@"*"];
[example3 calculate];
id<calculateDelegate> example4 = [SimpleFactory createCalcute:@"/"];
[example4 calculate];
结果:
可以看到这里创建了四个实例,但属于不同的产品类。
看一下简单工厂的实现:
+ (id)createCalcute:(NSString *)calculatetype {
NSArray *calculateArray = @[@"+",@"-",@"*",@"/"];
switch ([calculateArray indexOfObject:calculatetype]) {
case 0:
return [[MyAdd alloc]init];
break;
case 1:
return [[MyMinus alloc]init];
break;
case 2:
return [[MyMultiply alloc]init];
break;
case 3:
return [[MyDivide alloc]init];
}
return NULL;
}
使用一个类方法createCalcute:
返回不同类的实例对象。
工厂方法模式(Factory Method Pattern)
工厂方法也称为虛构造器(virtual constructor)。它适用于这种情况:一个类无法预期需要生成哪个类的对象,想让其子头来指定所生成的对象。而前面的简单工厂模式是能够明确生成哪个类对象的。
定义:
工厂模式:定义创建对象的接口,让子类决定实例化哪一个类,工厂方法使得一个类的实例化延迟到其子类。
使用场景:
下列情况会适合使用工厂模式:
- 编译时无法准确与其要创建的对象类
- 类想让其子类决定在运行时创建什么
- 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化
在Cocoa Touch
框架中,NSNumber
就是典型的工厂方法构造。NSNumber
通过类方法numberWithBool:
接口实例化NSCFBoolean
实例,将bool
值参数传递给实例化对象。
优势:
与直接创建新的具体对象相比,使用工厂方法创建对象可算作一种最佳做法。工厂方法模式让客户程序可以要求由工厂方法创建的对象拥有一组共同的行为。所以往类层次结构中引人新的具体产品井不需要修改客户端代码,因为返回的任何具体对象的接口都跟客户端一直在用的从前的接口相同。
问题:
该模式下产品和工厂的可扩展性都增强了,但是也随之而来的是可能会出现工厂类过多的问题。一般可以和简单工厂模式结合使用。
举例:
//IChart.h
@protocol IChart <NSObject>
- (void)drawing;
@end
//LineChart.m
@implementation LineChart
- (void)drawing {
NSLog(@"LineChart drawing.");
}
@end
//PieChart.m
@implementation PieChart
- (void)drawing {
NSLog(@"PieChart drawing.");
}
@end
//Factory.h
@protocol Factory <NSObject>
- (id<IChart>)createChart;
@end
//LineFactory.m
@implementation LineFactory
- (id<IChart>)createChart {
return [[LineChart alloc] init];
}
@end
//PieFactory.m
@implementation PieFactory
- (id<IChart>)createChart {
return [[PieChart alloc] init];
}
@end
抽象工厂模式(Abstract Factory Pattern)
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类。抽象工厂模式里每个工厂都会生产多种产品,但不同工厂生产的产品属于不同的系列。抽象工厂模式可以用来解决多产品族的问题。
解决的问题:
- 将一个系列的产品族统一到一起创建。
- 容易改变产品的系列
适用的场景:
需要创建一组对象,并需要切换不同的系列时。
缺点:
- 增加新的产品种类困难,它需要修改抽象工厂的接口。
- 代码结构比较复杂。
抽象工厂模式与工厂方法模式的区别:
工厂方法模式:
每个抽象产品派生多个具体产品类,每个抽象工厂类派生多个具体工厂类,每个具体工厂类负责一个具体产品的实例创建;
抽象工厂模式:
每个抽象产品派生多个具体产品类,每个抽象工厂派生多个具体工厂类,每个具体工厂负责多个(一系列)具体产品的实例创建。
类比解读
简单工厂:工厂可以创建同一系列的产品,产品的接口一致,但工厂就要根据参数进行判断到底创建哪种产品
卖早饭的张婆婆:可以做茶叶蛋,包子,稀饭
工厂方法:可以有多种工厂,工厂有共同的接口,一个工厂只能产生一种产品,比起简单工厂,工厂方法就不需要判断,耦合度低了不少
刘老板:只卖包子的包子铺,只卖稀饭的稀饭庄
抽象工厂:可以产生多个系列的产品,有2个维度的产品
KFC老板:可乐系列产品、汉堡系列产品,每种系列产品又分大,中,小三种。
如果这样来看应该很容易就能区分他们之间的关系了。
举例:
//MyFactory.m
@implementation MyFactory
+ (instancetype)factoryWithType:(MyFactoryProductType)type {
MyFactory *factory = nil;
switch (type) {
case MyFactoryProductTypeCar:
factory = [[CarFactory alloc] init];
break;
case MyFactoryProductTypePickupTruck:
factory = [[PickupTruckFactory alloc] init];
break;
default:
break;
}
return factory;
}
@end
//CarFactory.m
@implementation CarFactory
- (id<CarProtocol>)creatTypeI {
CarTypeI *car = [[CarTypeI alloc] init];
return car;
}
- (id<CarProtocol>)creatTypeII {
CarTypeII *car = [[CarTypeII alloc] init];
return car;
}
- (id<CarProtocol>)creatTypeIII {
CarTypeIII *car = [[CarTypeIII alloc] init];
return car;
}
@end
@implementation CarTypeI
- (void)carryPeople {
NSLog(@"TypeI can carry 5 people");
}
@end
@implementation CarTypeII
- (void)carryPeople {
NSLog(@"TypeII can carry 4 people");
}
@end
@implementation CarTypeIII
- (void)carryPeople {
NSLog(@"TypeIII can carry 3 people");
}
@end
//PickupTruckFactory.m
@implementation PickupTruckFactory
- (id<PickupTruckProtocol>)creatTypeI {
PickupTruckTypeI *car = [[PickupTruckTypeI alloc] init];
return car;
}
- (id<PickupTruckProtocol>)creatTypeII {
PickupTruckTypeII *car = [[PickupTruckTypeII alloc] init];
return car;
}
- (id<PickupTruckProtocol>)creatTypeIII {
PickupTruckTypeIII *car = [[PickupTruckTypeIII alloc] init];
return car;
}
@end
@implementation PickupTruckTypeI
- (void)carryGoods {
NSLog(@"TypeI can carry 3 tons");
}
@end
@implementation PickupTruckTypeII
- (void)carryGoods {
NSLog(@"TypeII can carry 4 tons");
}
@end
@implementation PickupTruckTypeIII
- (void)carryGoods {
NSLog(@"TypeIII can carry 5 tons");
}
@end
//客户端调用
MyFactory *factory = [MyFactory factoryWithType:MyFactoryProductTypeCar];
id<CarProtocol> vehicle = [factory creatTypeI];
[vehicle carryPeople];
id<CarProtocol> vehicle2 = [factory creatTypeII];
[vehicle2 carryPeople];
MyFactory *factory2 = [MyFactory factoryWithType:MyFactoryProductTypePickupTruck];
id<PickupTruckProtocol> vehicle3 = [factory2 creatTypeI];
[vehicle3 carryGoods];
id<PickupTruckProtocol> vehicle4 = [factory2 creatTypeII];
[vehicle4 carryGoods];
输出:
假设抽象工厂有一种新的产品线(SUVFactory)到来的时候,只需要修改factoryWithType:
,先前代码则不需要任何修改。