Builder Pattern 在 Objective-C 中的使用

在说 Builder Pattern 之前,我们先来看看一个场景。假设我们要预定一个 iPhone 6,要 64G 的,金色的,用代码表述大概是这样

1
2
3
4
// PFX 是一个前缀,因为直接写 iPhone6 不符合类名大写的习惯,写成 IPhone6 更是怪异 ╮(╯▽╰)╭
PFXiPhone6 *iphone = [[PFXiPhone6 alloc] init];
iphone.storage = 64;
iphone.color = [UIColor goldenColor];

也可以是另一种方式

1
PFXiPhone6 *iPhone = [[PFXiPhone6 alloc] initWithStorage:64 color:[UIColor goldenColor]];

第一种方式可扩展性好些,但无法约束必须设置某些 property。第二种方式修正了这个问题,但扩展性也差了。

假如又有了新需求,要让客户可以选择发售区域,比如港行,国行,美版等等。对于第一种,自然可以新增一个属性,但使用者很可能完全不知道新增了这么个属性。对于第二种,则只能再新建一个初始化方式,如 -[initWithStorage:color:place]。那如果又有新的需求,比如选择是否刻字,以及刻哪些字呢?或者可以选择外壳的种类等等。这两种方式都不能很好地处理需求的变更。

现在我们来说说 Builder Pattern,这个模式可以在各种语言里被很方便地实现,比如 javascript

1
2
3
4
5
new  PFXiPhone6Builder()
   .setStorage(64)
   .setColor( 'golden' )
   .setPlace( 'HK' )
   .build();

当有新的属性时,再加一个 setProperty 即可。如果漏写了某个属性,可以在 build 里检查。

在 Objective-C 里,这样的链式写法不是很流行(Masonry里这种写法用的比较多),所以,在 OC 里写起来大概会是这样

1
2
3
4
5
PFXiPhone6Builder *builder = [[PFXiPhone6Builder alloc] init];
builder.storage = 64;
builder.color = [UIColor goldenColor];
builder.place = @ "HK" ;
PFXiPhone6 *iphone = [builder build];

如果少了什么属性,在 build 时检查下即可。这样既解决了不方便扩展的问题,当有新的属性时也可以知道。

不过看起来还是不够优雅,这个 builder 只是一个临时工具,用完了就扔掉了,既然这样,那有没有可能写法上符合 OC 的传统,又让这个 builder 「临时出现」一下?且看下面这段代码

1
2
3
4
5
PFXiPhone6 *iPhone6 = [PFXiPhone6 createWithBuilder:^(PFXiPhone6Builder *builder){
     builder.storage = 64;
     builder.color = [UIColor goldenColor];
     builder.place = @ "HK" ;
}];

是不是看起来舒服多了。builder 只是在 block 范围内起作用,不会影响当前 context 的变量。这个 +[createWithBuilder:] 的代码如下

1
2
3
4
5
6
+ (instancetype)createWithBuilder:(BuilderBlock)block {
     NSParameterAssert(block);
     PFXiPhone6Builder *builder = [[PFXiPhone6Builder alloc] init];
     block(builder);
     return  [builder build];
}

这里 build 方法,也有两种实现,第一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// PFXiPhone6Builder
- (PFXiPhone6 *)build
{
     return  [[PFXiPhone6 alloc] initwithBuilder:self];
}
// PFXiPhone6
- (instancetype)initwithBuilder:(PFXiPhone6Builder *)builder
{
     if  (self = [ super  init]) {
         _storage = builder.storage;
         _color = builder.color;
         _place = builder.place;
     }
}

另外一种是把两个过程合并为一个过程

1
2
3
4
5
6
7
8
9
10
11
// PFXiPhone6Builder
- (PFXiPhone6 *)build
{
     // 可以在这里对 property 做检查
     NSAssert(self.place, @ "发行区别忘了填哦" );
     PFXiPhone6 *iphone6 = [[PFXiPhone6 alloc] init];
     iPhone6.storage = self.storage;
     iPhone6.color = self.color;
     iPhone6.place = self.place;
     return  iPhone6;
}

这两种方式的区别在于对参数的处理,前一个是在目标 Class 中处理,后一种是在 Builder 中处理。

在 Facebook 的 pop 中也有类似的使用,如

1
2
3
4
5
6
POPAnimatableProperty *animatableProperty = [POPAnimatableProperty propertyWithName:@ "property"  initializer:^(POPMutableAnimatableProperty *prop) {
     prop.writeBlock = ^(id obj, const CGFloat values[]) {
     };
     prop.readBlock = ^(id obj, CGFloat values[]) {
     };
}];

这里的 initializer 其实就是 builder

我在写蘑菇街的基础框架时,也有用到过几处,觉得还是蛮方便的,尤其对使用者来说。比如这个可以横向或纵向滚动的包含可点击 Items 的 collectionView。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.collectionView = [MGJFlowCollectionView collectionViewWithBuilder:^(MGJFlowCollectionViewBuilder *builder) {
     builder.scrollDirection = UICollectionViewScrollDirectionHorizontal;
     builder.minimumInteritemSpacing = 10;
     builder.minimumLineSpacing = 10;
     builder.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
     CGSize itemSize = CGSizeMake(81, 100);
     builder.itemSize = itemSize;
     builder.dataSource = @[@1,@2,@3,@4,@5,@6, @7,@8, @9, @10];
     builder.cellBuilder = ^UIView *(NSNumber *number){
         UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, itemSize.width, itemSize.height)];
         view.backgroundColor = [UIColor mgj_random];
         return  view;
     };
}];

这样就能通过简单的配置来生成一个水平的或垂直的 collectionView 了。

使用 Builder Pattern 还有一个好处,就是可以将零散的配置统一起来。比如要创建一个 CollectionView,我们需要设置 layout,还要设置 layout 的一些属性,还要设置 DataSource / Delegate 等,现在可以在一个地方统一设置,可读性上也会好一些。

所以如果遇到需要多个参数,甚至某个参数自己还包含了各种参数时,可以考虑下 Builder Pattern。

参考:http://www.annema.me/the-builder-pattern-in-objective-c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值