生成器模式(Builder Pattern):也叫创建者模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
具体点说就是:有些对象的创建流程是一样的,但是因为自身特性的不同,所以在创建他们的时候需要将创建过程和特性的定制分离开来。
下面我们看一下该设计模式的适用场景。
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时比较适合使用生成器模式。
一些复杂的对象,它们拥有多个组成部分(如汽车,它包括车轮、方向盘、发送机等各种部件)。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车。而且这些部分的创建顺序是固定的,或者是需要指定的。
在这种情况下可以通过建造者模式对其进行设计与描述,生成器模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。
建造者模式包含4个成员:
下面通过类图来看一下各个成员之间的关系:
生成器模式类图
需要注意的是:
protected
,目的是为了仅让它和它的子类可以访问该成员变量。constructProductWithBuilder(Builder builder)
方法是通过传入不同的builder来构造产品的。而且它的getProduct()
方法同时也封装了Concrete Builder
类的getProduct()
方法,目的是为了让客户端直接从Director
拿到对应的产品(有些资料里面的Director
类没有封装Concrete Builder
类的getProduct()
方法)。模拟一个制造手机的场景:手机的组装需要几个固定的零件:CPU,RAM,屏幕,摄像头,而且需要CPU -> RAM ->屏幕 -> 摄像头的顺序来制造。
我们使用建造者设计模式来实现这个场景:首先不同的手机要匹配不同的builder;然后在Director
类里面来定义制造顺序。
首先我们定义手机这个类,它有几个属性:
//================== Phone.h ==================
@interface Phone : NSObject
@property (nonatomic, copy) NSString *cpu;
@property (nonatomic, copy) NSString *capacity;
@property (nonatomic, copy) NSString *display;
@property (nonatomic, copy) NSString *camera;
@end
然后我们创建抽象builder类:
//================== Builder.h ==================
#import "Phone.h"
@interface Builder : NSObject
{
@protected Phone *_phone;
}
- (void)createPhone;
- (void)buildCPU;
- (void)buildCapacity;
- (void)buildDisplay;
- (void)buildCamera;
- (Phone *)obtainPhone;
@end
抽象builder类声明了创建手机各个组件的接口,也提供了返回手机实例的对象。
接下来我们创建对应不同手机的具体生成者类:
IPhoneXR手机的builder:IPhoneXRBuilder
:
//================== IPhoneXRBuilder.h ==================
@interface IPhoneXRBuilder : Builder
@end
//================== IPhoneXRBuilder.m ==================
@implementation IPhoneXRBuilder
- (void)createPhone{
_phone = [[Phone alloc] init];
}
- (void)buildCPU{
[_phone setCpu:@"A12"];
}
- (void)buildCapacity{
[_phone setCapacity:@"256"];
}
- (void)buildDisplay{
[_phone setDisplay:@"6.1"];
}
- (void)buildCamera{
[_phone setCamera:@"12MP"];
}
- (Phone *)obtainPhone{
return _phone;
}
@end
小米8手机的builder:MI8Builder
:
//================== MI8Builder.h ==================
@interface MI8Builder : Builder
@end
//================== MI8Builder.m ==================
@implementation MI8Builder
- (void)createPhone{
_phone = [[Phone alloc] init];
}
- (void)buildCPU{
[_phone setCpu:@"Snapdragon 845"];
}
- (void)buildCapacity{
[_phone setCapacity:@"128"];
}
- (void)buildDisplay{
[_phone setDisplay:@"6.21"];
}
- (void)buildCamera{
[_phone setCamera:@"12MP"];
}
- (Phone *)obtainPhone{
return _phone;
}
@end
从上面两个具体builder的代码可以看出,这两个builder都按照其对应的手机配置来创建其对应的手机。
下面来看一下Director的用法:
//================== Director.h ==================
#import "Builder.h"
@interface Director : NSObject
- (void)constructPhoneWithBuilder:(Builder *)builder;
- (Phone *)obtainPhone;
@end
//================== Director.m ==================
implementation Director
{
Builder *_builder;
}
- (void)constructPhoneWithBuilder:(Builder *)builder{
_builder = builder;
[_builder buildCPU];
[_builder buildCapacity];
[_builder buildDisplay];
[_builder buildCamera];
}
- (Phone *)obtainPhone{
return [_builder obtainPhone];
}
@end
Director类提供了
construct:
方法,需要传入builder的实例。该方法里面按照既定的顺序来创建手机。
最后我们看一下客户端是如何使用具体的Builder和Director实例的:
//================== Using by client ==================
//Get iPhoneXR
//1. A director instance
Director *director = [[Director alloc] init];
//2. A builder instance
IPhoneXRBuilder *iphoneXRBuilder = [[IPhoneXRBuilder alloc] init];
//3. Construct phone by director
[director construct:iphoneXRBuilder];
//4. Get phone by builder
Phone *iPhoneXR = [iphoneXRBuilder obtainPhone];
NSLog(@"Get new phone iPhoneXR of data: %@",iPhoneXR);
//Get MI8
MI8Builder *mi8Builder = [[MI8Builder alloc] init];
[director construct:mi8Builder];
Phone *mi8 = [mi8Builder obtainPhone];
NSLog(@"Get new phone MI8 of data: %@",mi8);
从上面可以看出客户端获取具体产品的过程:
construct:
方法。obtainPhone
获取手机实例。下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:
生成器模式代码示例类图
StringBuilder
属于builder,它向外部提供append(String)
方法来拼接字符串(也可以传入int等其他类型);而toString()
方法来返回字符串。