iOS创建对象的不同姿势详解

2020-01-18 20:44:12王冬梅

无论我们定义何种类型的 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。