2.1 定义
定义: 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
2.2 简单工厂,工厂方法与抽象工厂对比
简单工厂:工厂可以创建同一系列的产品,产品的接口一致,但工厂就要根据参数进行判断到底创建哪种产品
卖早饭的张婆婆:可以做茶叶蛋,包子,稀饭 工厂方法:可以有多种工厂,工厂有共同的接口,一个工厂只能产生一种产品,比起简单工厂,工厂方法就不需要判断,耦合度低了不少 刘老板:只卖包子的包子铺,只卖稀饭的稀饭庄 抽象工厂:可以产生多个系列的产品,有2个维度的产品 KFC老板:可乐系列产品、汉堡系列产品,每种系列产品又分大,中,小三种。如果这样来看应该很容易就能区分他们之间的关系了。
2.3 产品族
为了方便引进抽象工厂模式,引进一个新概念: 产品族(Product Family) 。所谓产品族,是指位于不同产品等级结构,功能相关联的产品组成的家族。如图:
图中一共有四个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
所谓的产品族,是指 位于不同产品等级结构中功能相关联的产品组成的家族 。
可能大家还是看不太懂举个例子吧: 比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:
显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:
那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。
可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。
2.4 抽象工厂模式的优点
(1)分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
(2)使切换产品族变得容易
因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。
2.5 抽象工厂模式的缺点
不太容易扩展新的产品: 如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。
2.6 抽象工厂结构
抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。
假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:
根据产品角色的结构图,就不难给出工厂角色的结构设计图。
可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。
抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。
由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。
这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换。
2.7 何时使用
(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
(2)这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
(3)同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)
(4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
2.8 实例代码
下面我们来看看上面这个实例的代码:
CPU 父类:
#import/*! * CPU父类 * * @since V1.0 */@interface CpuBase : NSObject@property int pins;//针脚数 -(void)calculate; @end
Intel 的 CPU 子类:
#import "CpuBase.h"/*! * Intel的CPU子类 * * @since V1.0 */@interface IntelCPU : CpuBase @end
MainBoard 父类:
#import/*! * MainBoard父类 * * @since V1.0 */@interface MainBoardBase : NSObject@property int cpuHoles;//CPU插槽数 -(void)installCpuHoles;//统计CPU插槽数 @end
Intel 的主板子类:
#import "MainBoardBase.h"/*! * Intel的主板子类 * * @since V1.0 */@interface IntelMainBoard : MainBoardBase @end
工厂基类:
#import#import "CpuBase.h"#import "MainBoardBase.h"/*! * 工厂基类 * * @since V1.0 */ @interface FactoryBase : NSObject /*! * 创建CPU * @return 返回CPU实例 * * @since V1.0 */ -(CpuBase*)createCpu; /*! * 创建主板 * * @return 返回主板实例 * * @since V1.0 */ -(MainBoardBase*)createMainBoard; @end
Intel 工厂子类:
#import "FactoryBase.h"#import "IntelCPU.h"#import "IntelMainBoard.h"/*! * Intel工厂子类 * * @since V1.0 */ @interface IntelFactory : FactoryBase @end
电脑工程师类:
#import#import "CpuBase.h"#import "MainBoardBase.h" #import "FactoryBase.h" /*! * 电脑工程师类 * * @since V1.0 */ @interface ComputerEngineer : NSObject @property(nonatomic,retain)CpuBase* cpu;//CPU基类 @property(nonatomic,retain)MainBoardBase* mainBoard;//主板基类 -(void)makeComputer:(FactoryBase*)factoryBase; /*! * 组装硬件 * * @param factoryBase 工厂基类 * * @since V1.0 */ -(void)prepareHardwares:(FactoryBase*)factoryBase; @end
客户端类:
- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view, typically from a nib. //创建装机工程师对象 ComputerEngineer* cf = [[ComputerEngineer alloc] init]; //客户选择并创建需要使用的产品对象 FactoryBase* af = [[IntelFactory alloc] init]; // FactoryBase* af = [[AMDFactory alloc] init]; //告诉装机工程师自己选择的产品,让装机工程师组装电脑 [cf makeComputer:af]; }