天天看點

【原】iOS設計模式之:建造者模式Builder Pattern,用于改進初始化參數

本文主要讨論一下iOS中的Builder Pattern。與網上很多版本不同,本文不去長篇大論地解釋建造者模式的概念,那些東西太虛了。設計模式這種東西是為了解決實際問題的,不能為了設計模式而設計模式,雖然這句話有點拗口!我希望我們都能宏觀地看待某個設計模式,不必去太可以追求概念上的東西。事實上,隻要你懂得如何應用,那此模式彼模式叫什麼名稱已經無所謂了。

我們先來看個例子,假設你現在要買一輛車,提出以下一堆要求:白色、價格10萬以内、必須是國産車(愛國是必須的)、5座...,用iOS代碼描述就是這樣的:

1         WZLCar *myCar = [[WZLCar alloc] init];
2         myCar.color = [UIColor whiteColor];
3         myCar.price = 100000;
4         myCar.family = @"China";
5         myCar.seatCount = 5;
6         //...more properties      

或者是這樣:

1 WZLCar *myCar = [WZLCar alloc] initWithColor:[UIColor whiteColor] price:100000 family:@"China" seatCount:5 ....];      

實際上,以上兩種方式分别代表了兩種iOS對象的初始化方式。在未使用Builder Pattern之前,這兩種初始化對象方式都有不友善的地方。第一種方法靈活,但是如果你接觸iOS時間長點就會發現,經常會記不住這個類到底有哪些property需要初始化,尤其是當這個類是被人提供給你的時候!我們多麼希望類的提供者能搞點注釋啊啥的告知我們一下。第二種方法則很清晰地告訴調用者到底有哪些property需要在建立對象時初始化。但是,當類的提供者有天跟你說:hey,哥們,現在業務邏輯改變了,我增加了一個xxx屬性,init方法也變了。這時,你是不是很想打他?每新增一個新的屬性,init方法就要變動,而且當需要初始化的屬性多大十幾二十個時,這個init方法要不要太壯觀!

是以,當某個類的屬性值很多時,我們可以考慮使用建造者模式Builder Pattern來讓初始化過程清晰一些,類的使用者會很感恩你這麼做的。用iOS描述大概是這樣的:

1 WZLCarBuilder *builder = [[WZLCarBuilder alloc] init];//builder裡面列出了所有需要初始化的參數,可以認為是一個to-do list
2 builder.color=[UIColor whiteColor];
3 builder.price=100000;
4 builder.family=@"China";
5 builder.seatCount=5;
6 WZLCar *car=[builder build];//build方法産生一個WZLCar執行個體      

其中WZLCarBuilder類的property将WZLCar類中需要初始化的property複制了一遍。當你在執行個體化一個WZLCar對象而忘記那些參數時,可以跳轉到WZLCarBuilder類的頭檔案看一下就一目了然了。從這種角度看,builder其實就是一個to-do list供類的調用者查閱。有強迫症的同學可以對以上代碼并不感冒,因為它顯得不夠緊湊不夠優雅,在初始化一個WZLCar對象前要搞一堆代碼。有強迫症的coder大部分都是好程式員,那我們就嘗試讓它更優雅一些。我們可以這麼做:

1         WZLCar *myCar = [WZLCar creatWithBuilder:^(WZLCarBulider *builder){
2             builder.color = [UIColor whiteColor];
3             builder.price = 50000;
4             builder.family = @"China";
5             builder.seatCount = 4;
6         }];
7         NSLog(@"myCar:%@", [myCar description]);      

将builder的配置資訊封裝到block中,這樣代碼整體看起來緊湊很多,也給類的調用者很多提示資訊。WZLCar的 + (WZLCar *)creatWithBuilder:(WZLCarBuliderBlock)block 方法實作如下,詳見代碼注釋部分:

1 + (WZLCar *)creatWithBuilder:(WZLCarBuliderBlock)block
2 {
3     NSParameterAssert(block != nil);//參數單元測試是必須的
4     WZLCarBulider *bulider = [[WZLCarBulider alloc] init];
5     block(bulider);//這裡的builder是一個指針,block内部對其所作的改變都會被保留下來
6     return [bulider build];//build建立一個WZLCar執行個體
7 }      

WZLCarBuliderBlock是一個block聲明: 

typedef void (^WZLCarBuliderBlock) (WZLCarBulider *bulder);      

剩下的工作就是對WZLCarBuilder的build方法實作了:

1 - (WZLCar *)build
 2 {
 3     NSAssert(self.color, @"color property is forcely to be initilized!");
 4     WZLCar *car = [[WZLCar alloc] init];
 5     car.color = self.color;
 6     car.price = self.price;
 7     car.family = self.family;
 8     car.seatCount = self.seatCount;
 9     return [car autorelease];
10 }      

一些需要調用者強制初始化的參數可以build函數的開頭處添加斷言,一旦WZLCar類的調用者在初始化時沒有初始化color就是斷言失敗抛出異常。這在多人協同開發時可以省事很多。下面我們像文章開頭一般,調用一下WZLCar類:

1 WZLCar *myCar = [WZLCar creatWithBuilder:^(WZLCarBulider *builder){
2     builder.color = [UIColor whiteColor];
3     builder.price = 50000;
4     builder.family = @"China";
5     builder.seatCount = 4;
6 }];
7 NSLog(@"myCar:%@", [myCar description]);      

Log确實列印出相應的資訊,我就截圖了,這就說明builder pattern确實讓WZLCar的初始化産生效果。按照上面的代碼,如果WZLCar類的使用者一時疏忽忘記初始化color屬性,程式會在斷言處抛異常:

WZLCar *myCar = [WZLCar creatWithBuilder:^(WZLCarBulider *builder){
    //builder.color = [UIColor whiteColor];
    builder.price = 50000;
    builder.family = @"China";
    builder.seatCount = 4;
}];
NSLog(@"myCar:%@", [myCar description]);      
【原】iOS設計模式之:建造者模式Builder Pattern,用于改進初始化參數

===================================

在FaceBook的開源動畫架構POP中也有對builder pattern類似的應用:

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

這裡的initializer本質上就是builder,隻是叫法不同而已。

PS:從本文的builder pattern實作中你也可以抽象出一些東西應用在其他方面,比如利用block來封裝一些屬性的配置資訊,比如iOS8中新增加的UIAlertController類就用到這種思想,是用block讓調用者在一個block内給出對UITextField的配置:

【原】iOS設計模式之:建造者模式Builder Pattern,用于改進初始化參數

##THAT IS ALL.

=======================================

原創文章,轉載請注明 程式設計小翁@部落格園,郵件[email protected],微信Jilon,歡迎各位與我在C/C++/Objective-C/機器視覺等領域展開交流!

 =======================================