无论我们定义何种类型的 class,给 class 中的每个 property 都赋予一个初始值是个很好的习惯,可以避免掉一些意外的 bug 产生,这也是 designated initializer 的重要职责。
在实际的项目当中,一个 class 的 property 数目可能会随着业务的增长而增加,最后的结果就是会生成越来越多的 convenience initializer。上述的 User 类,如果是 3 个 property,极端的情况下最多可以有 7 个 init 方法。Peak君在阅读代码的时候,也确实看到过有些 class 定义了一连串整整齐齐摆放的 init 方法,代码虽然看着规范,但显得啰嗦,而且每次需要肉眼搜索适合的 init 方法。
其实我们还可以用另一种姿势来 init 我们的对象。
Builder pattern
最初是在学习 Android 的时候,发现这个 builder pattern 也可以用来构建对象,而且可以很好的解决 init 方法过多难以管理的问题。先来看下如何实现,顾名思义,builder pattern 使用另一个名为 builder 的类来创建我们的目标对象,还是上面的例子,代码如下:
//UserBuilder.h
@interface UserBuilder : NSObject
@property (nonatomic, strong, readonly) NSNumber* userID;
@property (nonatomic, strong, readonly) NSString* userName;
@property (nonatomic, strong, readonly) NSString* signature;
- (UserBuilder*)userID:(NSNumber*)userID;
- (UserBuilder*)userName:(NSString*)userName;
- (UserBuilder*)signature:(NSString*)signature;
@end
//UserBuilder.m
@implementation UserBuilder
- (UserBuilder*)userID:(NSNumber*)userID {
_userID = userID;
return self;
}
- (UserBuilder*)userName:(NSString*)userName {
_userName = userName;
return self;
}
- (UserBuilder*)signature:(NSString*)signature {
_signature = signature;
return self;
}
@end
接下来 User 的 init 方法从 Builder 中获取 property 的初始值:
//User.h
@interface User : NSObject
@property (nonatomic, strong, readonly) NSNumber* userID;
@property (nonatomic, strong, readonly) NSString* userName;
@property (nonatomic, strong, readonly) NSString* signature;
- (instancetype)initWithUserBuilder:(UserBuilder*)builder;
@end
//User.m
@implementation User
- (instancetype)initWithUserBuilder:(UserBuilder*)builder {
self = [super init];
if (!self) {
return nil;
}
_userID = builder.userID;
_userName = builder.userName;
_signature = builder.signature;
return self;
}
@end
如果要创建 User 对象,则按照这种方式:
UserBuilder* builder = [[[[UserBuilder new] userName:@"peak"] userID:@1000] signature:@"roll"];
User* user = [[User alloc] initWithUserBuilder:builder];
这样我们避免了书写多个 init 方法,同样 User 对象也是 immutable 的,也做到了只在 init 方法中做一次赋值操作,每个场景都可以按照自己的需求初始化部分 property,当然最后我们需要在 initWithUserBuilder 中为每一个 property 赋值, initWithUserBuilder 扮演的角色类似于 designated initializer。










