抽象工厂模式,提供一个创建一系列相关或相互依赖的接口
优点:
- 易于交换产品系列
- 让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中
用户类 User
///===================User.h=================
/// 用户类
@interface User : NSObject
/// 用户 id
@property (nonatomic, copy) NSString *userId;
/// 用户名
@property (nonatomic, copy) NSString *username;
@end
/// ===============User.m===================
@implementation User
@end
部门类 Department
///===============Department.h===========
/// 部门类
@interface Department : NSObject
/// 部门 id
@property (nonatomic, copy) NSString *departmentId;
/// 部门名称
@property (nonatomic, copy) NSString *departmentName;
@end
@implementation Department
@end
用于访问用户的接口 IUser
/// 用于访问用户的接口
@protocol IUser
/// 新增用户
/// @param user 用户对象
- (void)insert:(User *)user;
/// 获取用户
/// @param userId 用户 id
/// @return 用户对象
- (User *)getUser:(NSString *)userId;
@end
用于访问 SQL Server 中的用户 SQLServer
///==============SQLServerUser.h===========
/// 用于访问 SQL Server 中的用户
@interface SQLServerUser : NSObject
@end
///==============SQLServerUser.m============
@implementation SQLServerUser
- (void)insert:(User *)user {
NSLog(@"在 SQL Server 中给 User 表增加一条记录");
}
- (User *)getUser:(NSString *)userId {
NSLog(@"在 SQL Server 中根据 userId 得到 User 表一条记录");
return nil;
}
@end
用于访问 Access 中的用户AccessUser
///=============AccessUser.h==============
/// 用于访问 Access 中的用户
@interface AccessUser : NSObject
@end
///=============AccessUser.m==========
@implementation AccessUser
- (void)insert:(User *)user {
NSLog(@"在 Access 中给 User 表增加一条记录");
}
- (User *)getUser:(NSString *)userId {
NSLog(@"在 Access 中根据 userId 得到 User 表一条记录");
return nil;
}
@end
用于访问部门的接口IDepartment
/// 用于访问部门的接口
@protocol IDepartment
/// 新增部门
///
/// @param department 部门对象
- (void)insert:(Department *)department;
/// 获取部门
///
/// @param departmentId 部门 id
///
/// @return 部门对象
- (Department *)getDepartment:(NSString *)departmentId;
@end
用于访问 SQL Server 中的部门SQLServerDepartment
///==============SQLServerDepartment.h=========
/// 用于访问 SQL Server 中的部门
@interface SQLServerDepartment : NSObject
@end
///==============SQLServerDepartment.m=========
@implementation SQLServerDepartment
- (void)insert:(Department *)department {
NSLog(@"在 SQL Server 中给 Department 表增加一条记录");
}
- (Department *)getDepartment:(NSString *)departmentId {
NSLog(@"在 SQL Server 中根据 departmentId 得到 Department 表一条记录");
return nil;
}
用于访问 Access 中的部门 AccessDepartment
///=============AccessDepartment.h===========
/// 用于访问 Access 中的部门
@interface AccessDepartment : NSObject
@end
=============AccessDepartment.h===========
@implementation AccessDepartment
- (void)insert:(Department *)department {
NSLog(@"在 Access 中给 Department 表增加一条记录");
}
- (Department *)getDepartment:(NSString *)departmentId {
NSLog(@"在 Access 中根据 departmentId 得到 Department 表一条记录");
return nil;
}
@end
抽象工厂接口IFactory.h
/// 抽象工厂接口
@protocol IFactory
/// 创建用于访问用户的对象
///
/// @return 用于访问用户的对象
- (id)createUser;
/// 创建用于访问部门的对象
///
/// @return 用于访问部门的对象
- (id)createDepartment;
@end
创建用于访问 SQL Server 对象的工厂类SQLServerFactory
///==============SQLServerFactory.h============
/// 创建用于访问 SQL Server 对象的工厂类
@interface SQLServerFactory : NSObject
@end
///==============SQLServerFactory.m============
@implementation SQLServerFactory
- (id)createUser {
return [[SQLServerUser alloc] init];
}
- (id)createDepartment {
return [[SQLServerDepartment alloc] init];
}
@end
创建用于访问 Access 对象的工厂类 AccessFactory
/// ================AccessFactory.h==========
/// 创建用于访问 Access 对象的工厂类
@interface AccessFactory : NSObject
@end
/// ================AccessFactory.m==========
@implementation AccessFactory
- (id)createUser {
return [[AccessUser alloc] init];
}
- (id)createDepartment {
return [[AccessDepartment alloc] init];
}
@end
类图
AbstractProductA 和 AbstractProductB 是两个抽象产品, 只定义了接口,可以有不同的实现方式, 先相当于User 和 Department, ProductA1、 ProductA2、 ProductB1 ProductB2就是对两个抽象产品的实现
同理, IFactory 是一个抽象接口,它里面应该包含所有产品的创建的抽象方法, ConcreteFactory1 ConcreteFactory2 就是具体工厂
通常实在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象, 也就是说, 为创建不同的产品对象,客户端使用不同的具体工厂
抽象工厂与工厂方法比较
抽象工厂 | 工厂方法 |
---|---|
通过对象组合创建抽象产品 | 通过类继承创建抽象产品 |
创建多系列产品 | 创建一种产品 |
必须修改父类接口才能支持新的产品 | 子类化创建者并重载工厂方法以创建新产品 |
iOS中的抽象工厂
在iOS中,有很多基础类采用了这一模式,常见的就是NSNumber
。创建NSNumber实例的方法完全符合抽象工厂模式
NSNumber *boolNumber = [NSNumber numberWithBool:YES];
NSNumber *charNumber = [NSNumber numberWithChar:'a'];
NSNumber *intNumber = [NSNumber numberWithInt:1];
NSNumber *floatNumber = [NSNumber numberWithFloat:1.0];
NSNumber *doubleNumber = [NSNumber numberWithDouble:1.0];
接受不同类型的参数并返回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:
这样的工厂方法并不是为了返回产品,而是为了返回能返回产品的工厂,因此它们不应在具体子类重载。
总结
抽象工厂是一种常见的设计模式。它是最基本的,因为它可以设计许多类型的对象创建。一系列相关类好的设计模式,应该作为一种抽象,不为客户端所见。抽象工厂可以顺畅地提供这种抽象,而不暴露创建过程中任何不必要的细节或所创建对象的确切类型。