定義
"將一個複雜物件的構建與它的表現分離,使得同樣的構建過程可以建立不同的表現"。
看這個概念,可能感覺很是抽象,能看懂但是不知道有什麼用。我們打一個比方來理解上面的定義。打比方之前,咱們先來聊聊這個設計模式是幹什麼用的?我們為什麼要用這個模式呢?建造者模式負責將構建複雜物件的過程和它的組件解耦,也就是過程和組件的解耦。比如說汽車,是一個很複雜的對象,它有很多的組件,車輪、發動機、座椅、車門、油箱等等;它的組裝過程也很複雜(需要專業人士按步驟進行裝配),建造者模式就是為了將組件和組裝過程分開的。同樣的,我們使用的電腦也一樣,有很多的組件,組裝過程也很複雜(當然,對於我們這樣的專業人士可能感覺不複雜)。建造者模式最大的好處就是使得構建過程和表現分離,因此若需要改變一個產品的表現,只需要重新定義一個具體的建造者就可以了(這句話理解起來有點難度,還是拿車來打比方,我們將車的組裝過程獨立出來,用這個組裝過程,我們即可以組裝寶馬車,也可以組裝平治車,或者其他的車型,我們只需要重新定義一個具體的建造者(用於產品表現的類)就可以了)。
動機
在軟體系統中,有時候會遇到一個複雜物件(比如說上面例子中的汽車)的建立,它通常由幾個部分的子物件採用一定的演算法(過程)構成;由於需求的變化,這個複雜物件的各個部分經常面臨著劇烈的變化(比如上面例子中,各種車型用的車門、方向盤、發動機等,是不一樣的),但是將各個部分組合在一起的演算法(過程)是相對穩定的。
建造者模式就是在這樣的需求下誕生的,它封裝了變化點(組成部分),使得同樣的構建過程可以建立不同的表現。
建造者模式是當在建立複雜物件的演算法應該獨立於該對象的組成部分以及它們的裝配方式時適用的模式。
建造者模式包含產品類(Product)、抽象建造者類(Builder)、具體建造者類(ConcreteBuilder1、ConcreteBuilder2…)和指揮者類(Director)
從下面的代碼中看各個類的使用:
複製代碼 代碼如下:
PersonBuilder *builder = [[PersonThinBuilder alloc]init];
PersonView *personView = [PersonDirector creatPerson:builder];
其中 PersonBuilder 是抽象建造者類,PersonThinBuilder 是具體建造者類,PersonView 是產品類,PersonDirector 是指揮者類。
由代碼瞭解使用方式:
1、建立具體建造者。
2、指揮者通過具體建造者來返回產品。
感覺建造者模式與Factory 方法模式非常類似,但是加入了指揮者類。
結構圖
從結構圖可以看到,產生器模式有兩個重要的角色:Director(指導者)和Builder(建造者)。Director知道Builder應該建造什麼(建造的過程),Builder知道如何建造(表現)。在Director類中定義了一個construct方法,指導具體的建造者ConcreteBuilder的對象去buildPart。Builder是一個抽象介面(協議),該協議中包含建造各個部分的方法(buildPart),用來構建實際的產品Product,另外還有一個getResult方法,用來向用戶端返回構建完成的Product。
這樣說不知道大家是不是感覺很抽象?那咱們用一個生活的例子來通俗點說。比如現在我要在老家修一個房子,首先我不知道怎麼修房子(砌牆,缺建造者),然後我也不知道怎麼設計(修幾個房間,房間的布局,房間的窗戶怎麼設計等等,缺指導者),於是我找來一幫民工(建造者),他們會砌牆;另外我還得找設計師(指導者),他知道怎麼設計;最後,我還要確保民工(建造者)聽設計師(指導者)的指導,哪裡需要砌一堵牆,哪裡需要安裝窗戶等等,這樣民工(建造者)就開始蓋房子,在這個建造過程中,設計師(指導者)只負責設計和下達命令。房子建成功後,民工(建造者)向我交房。說白了,就是Director(指導者)負責把控宏觀方面(過程),Builder(建造者)負責把控微觀方面(表現) 。
下面咱們還是通過代碼來說明這個結構圖。
執行個體
建造者模式的代碼
Product.m(部分代碼):
複製代碼 代碼如下:
- (id)init
{
self = [superinit];
if (self)
{
arrParts = [NSMutableArrayarray];
}
returnself;
}
- (void)addPart:(NSString *)part
{
[arrPartsaddObject:part];
}
- (void)show
{
for (NSString *strPart inarrParts)
{
NSLog(@"%@",strPart);
}
}
Builder.h(部分代碼):
複製代碼 代碼如下:
@classProduct;
@protocol Builder <NSObject>
- (void)addPartOne;
- (void)addPartTwo;
- (Product *)getResult;
@end
ConcreteBuilder.m(部分代碼):
複製代碼 代碼如下:
- (id)init
{
self = [superinit];
if (self)
{
product = [[Productalloc] init];
}
returnself;
}
- (void)addPartOne
{
[productaddPart:@"part one"];
}
- (void)addPartTwo
{
[productaddPart:@"part two"];
}
- (Product *)getResult
{
returnproduct;
}
Director.m(部分代碼):
- (void)construct:(id<Builder>)builder
{
[builder addPartOne];
[builder addPartTwo];
}
用戶端調用代碼:
複製代碼 代碼如下:
Director *director = [[Directoralloc] init];
id<Builder> builder = [[ConcreteBuilderalloc] init];
[director construct:builder];
Product *product = [builder getResult];
[product show];
[builder release];
[director release];
何時使用建造者模式
建造者模式常用於如下情形:
需要建立涉及各種組件的複雜物件。建立對象的演算法應該獨立於組件的裝配方式。
構建過程需要以不同的方式構建對象。
PS:在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,只是叫法不同而已。