iOS模式之一:工厂模式和抽象工厂模式
简单工厂模式
工厂模式我的理解是:他就是为了创建对象的
创建对象的时候,我们一般是alloc一个对象,如果需要创建100个这样的对象,如果是在一个for循环中还好说,直接一
句alloc就行了,但是事实并不那么如意,我们可能会在不同的地方去创建这个对象,那么我们可能需要写100句alloc
了,但是如果我们在创建对象的时候,需要在这些对象创建完之后,为它的一个属性添加一个固定的值,比方说都是某某学校的学生,那么可能有需要多些100行
重复的代码了,那么,如果写一个-(void)createObj方法,把创建对象和学校属性写在这个方法里边,那么就是会省事很多,也就是说我们可以
alloc
创建对象封装到一个方法里边,直接调用这个方法就可以了,这就是简单工厂方法
代码结构如下
Animal 类
@interface Animal :NSObject
@proterty(nonatomic,strong) NSString *name;
-(void)laugh;
@end
Dog类
@interface Dog:Animal
@end
Cat类
@interface Cat:Animal
@end
创建对象的工厂类
.h
@interface AnimalFactory:NSObject
+(Dog *)createDog;
+(Cat *)createCat;
@end
.m
@implementation AnimalFactory
+(Dog *)createDog{
Dog *dog=[[Dog alloc]init];
dog.name=@“baby”;
return dog;
}
+(Cat *) createCat{
Cat *cat=[[Cat alloc]init];
return cat;
}
Main.m文件
Dog *dog=[AnimalFactory createDog];
Cat *cat=[AnimalFactory createCat];
这是简单工厂模式
现在创建100个Dog对象,如果这100个对象写在程序中的不同地方,按上边的方法是需要把Dog
*dog=[AnimalFactory
createDog];这一句话写在程序中很多不同的地方,那么现在有一个需求,就是如果需要把这些创建的100个Dog对象全部变成Cat对象,那么按
照刚才的那个做法,就需要在这100句代码中把createDog方法变成createCat方法了,这样做还是很复杂
那么这个时候用工厂方法模式就能解决这个难题了
工厂方法模式是为每一个要创建的对象所在的类都相应地创建一个工厂
代码如下
@interface AnimalFactory:NSObject
-(Animal*)createAnimal;
@end;
Dog工厂类
@interface DogFactory:AnimalFactory;
@implementation DogFactory
-(Animal *)createAnimal{
retrurn [[Dog alloc]init];
}
@end
Cat工厂类
@interface CatFactory:AnimalFactory;
@implementation Cat Factory
-(Animal *)createAnimal
retrurn [[Cat alloc]init];
}
@end
Main.m
AnimalFactory *dogFactory=[[DogFactory alloc]init];
Animal *animal1=[dogFactory createAnimal];
[animal1 laugh];
Animal *animal2=[dogFactory createAnimal];
[animal2 laugh];
…….
Animal *animal100=[dogFactory createAnimal];
[animal100 laugh];
这样话如果要把100个Dog改为Cat的话,只需要吧DogFactory改为CatFactory就可以了。
抽象工厂模式(Cocoa Touch框架中)
何为抽象工厂?
抽象工厂提供一个固定的接口,用于创建一系列有关联或相依存的对象,而不必指定其具体类或其创建的细节。客户端与从工厂得到的具体对象之间没有耦合。
抽象工厂与工厂方法模式的区别
抽象工厂与工厂方法模式在许多方面有很多相似之处,以至于我们常常搞不清楚应该在什么时候用哪一个。两个模式都用于相同的目的:创建对象而不让客户端知晓返回了什么确切的具体对象。
抽象工厂:@、通过对象组合创建抽象产品。@、创建多系列产品。@、必须修改父类的接口才能支持新的产品。
工厂方法:@、通过类继承创建抽象产品。@、创建一种产品。@、子类化创建并重载工厂方法以创建新产品。
在Cocoa Touch框架中使用抽象工厂
在Cocoa Touch框架中可以经常看到抽象工厂模式,很多基础类都采用了这一模式。比如NSNumber。创建NSNumber实例的方式完全符合抽象工厂模式。
创建对象有两种方式,使用先alloc再init的方法,或者使用类中的+className...方法。NSNumber类有很多类方法用于创建各种类型的NSNumber对象,如下:
NSNumber*boolNumber = [NSNumbernumberWithBool:YES];NSNumber*intNumber = [NSNumbernumberWithInt:10];NSNumber*floatNumber = [NSNumbernumberWithFloat:10.0];NSNumber*doubleNumber = [NSNumbernumberWithDouble:10.0];
每个返回的对象属于代表最初输入值的不同私有子类,可以用NSLog输出它们类的描述:
NSLog(@"%@", [[boolNumberclass]description]);
NSLog(@"%@", [[intNumberclass]description]);
NSLog(@"%@", [[floatNumberclass]description]);
NSLog(@"%@", [[doubleNumberclass]description]);
将看到如下的日志信息:
2015-09-0621:25:13.116NSNumberDemo[59970:4475507]__NSCFBoolean
2015-09-0621:25:13.117NSNumberDemo[59970:4475507]__NSCFNumber
2015-09-0621:25:13.117NSNumberDemo[59970:4475507]__NSCFNumber
2015-09-0621:25:13.117NSNumberDemo[59970:4475507]__NSCFNumber
除了boolNumber的实际类型是NSCFBoolean以外,大多数实际类为NSCFNumber类型。尽管这些+className类工厂方法返回NSNumber具体子类的实例,但是返回的实例确实支持NSNumber的公有接口。
虽然它们属于NSNumber的不同具体子类,但是其行为由抽象超类NSNumber定义,而且是公有的。如下代码所示:
NSLog(@"%d", [boolNumberintValue]);
NSLog(@"%@", [floatNumberboolValue] ? @"YES": @"NO");
将看到日志信息如下:
2015-09-0621:32:59.234NSNumberDemo[60043:4478868]1
2015-09-0621:32:59.234NSNumberDemo[60043:4478868]YES
boolNumber在内部保持布尔值YES,但仍实现了公有intValue方法,返回其内部布尔值的适当整数值。floatNumber也是如此,它重载了boolValue方法,返回反映其内部浮点型值的适当布尔值。
接受不同类型的参数并返回NSNumber实例的类方法是类工厂方法(工厂模式)。NSNumber的类工厂方法生产各种数工厂。numberWithBool:创建NSCFBoolean工厂的实例,而numberWithInt:创建NSCFNumber的实例。NSNumber中的类工厂方法定义了决定实例化何种私有具体子类(比
如,NSCFBoolean或NSCFNumber)的默认行为。这一版本的工厂方法是传统工厂方法模式的一个变体,此处的抽象产品为作为工厂的具体
NSNumber子类。NSNumber是抽象工厂实现的一个例子。基础框架中抽象工厂的此种特点被称为类簇。
类簇是基础框架中一种常见的设计模式,基于抽象工厂模式的思想。它将若干相关的私有具体工厂子类集合到一个公有的抽象超类之下。比如,数包含了各种数值类型的完整集合,如字符、浮点数、整数等。这些数值类型都是数的子集。所以NSNumber自然成为这些数子类型的超类型。NSNumber有一系列公有API,定义了各种类型的数所共有的行为。客户端在使用时无需知道NSNumber实例的具体类型。
类簇是抽象工厂的一种形式。比如,NSNumber本身是一个高度抽象的工厂,NSCFBoolean和NSCFNumber是具体工厂子类。子类是具体工厂,因为它们重载了NSNumber中声明的公有工厂方法以生产产品。
例如,intValue和boolValue根据实际NSNumber对象的内部值返回一个值,虽然值的数据类型可能不同。从这些工厂方法返回的实际值就是抽象工厂模式的最初定义中的所说产品。
创建抽象产品的工厂方法与创建抽象工厂的工厂方法之间有个不同点。显然,像intValue和boolValue这样的工厂方法,应该在具体工厂(NSCFNumber与NSCFBoolean)中重载以返回产品。而像numberWithBool:和numberWithInt:这样的工厂方法并不是为了返回产品,而是为了返回能返回产品的工厂,因此它们不应在具体子类重载。
具体代码实现
简单工厂,工厂方法与抽象工厂对比:
简单工厂:工厂可以创建同一系列的产品,产品的接口一致,但工厂就要根据参数进行判断到底创建哪种产品
卖早饭的张婆婆:可以做茶叶蛋,包子,稀饭
工厂方法:可以有多种工厂,工厂有共同的接口,一个工厂只能产生一种产品,比起简单工厂,工厂方法就不需要判断,耦合度低了不少
刘老板:只卖包子的包子铺,只卖稀饭的稀饭庄
抽象工厂:可以产生多个系列的产品,有2个维度的产品
KFC老板:可乐系列产品、汉堡系列产品,每种系列产品又分大,中,小三种。
如果这样来看应该很容易就能区分他们之间的关系了。
产品族:
为了方便引进抽象工厂模式,引进一个新概念:产品族(Product Family)。所谓产品族,是指位于不同产品等级结构,功能相关联的产品组成的家族。如图:
图中一共有四个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。
可能大家还是看不太懂举个例子吧:比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:
显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品
按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。
那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。
可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。
抽象工厂模式的优点:
(1)分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
(2)使切换产品族变得容易
因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。
抽象工厂模式的缺点:
不太容易扩展新的产品:如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。
抽象工厂结构:
抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。
假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:
根据产品角色的结构图,就不难给出工厂角色的结构设计图。
可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。
抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。
由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。
这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换。
何时使用:
(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
(2)这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
(3)同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)
(4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
实例代码:
下面我们来看看上面这个实例的代码:
CPU父类:
MainBoard父类
Intel的主板子类:
工厂基类:
Intel工厂子类:
电脑工程师类:
客户端类:
总结
抽象工厂模式是一种极为常见的设计模式。它是最基本的,因为它可以涉及许多类型的对象创建。一系列相关的类,应该作为一种抽象,不为客户端所见。抽象工厂则可以方便的解决这个问题,而不暴露创建过程中任何不必要的细节或所创建对象的确切类型。
参考博客:
http://www.tuicool.com/articles/rYFJRj
http://blog.csdn.net/xiazailushang/article/details/19040117
http://my.oschina.net/daguoshi/blog/501928?fromerr=hRCeeQMW
http://bbs.9ria.com/thread-243666-1-1.html