定義
"將一個復雜對象的構建與它的表現分離,使得同樣的構建過程可以創建不同的表現"。
看這個概念,可能感覺很是抽象,能看懂但是不知道有什么用。我們打一個比方來理解上面的定義。打比方之前,咱們先來聊聊這個設計模式是干什么用的?我們為什么要用這個模式呢?建造者模式負責將構建復雜對象的過程和它的部件解耦,也就是過程和部件的解耦。比如說汽車,是一個很復雜的對象,它有很多的部件,車輪、發動機、座椅、車門、油箱等等;它的組裝過程也很復雜(需要專業人士按步驟進行裝配),建造者模式就是為了將部件和組裝過程分開的。同樣的,我們使用的計算機也一樣,有很多的部件,組裝過程也很復雜(當然,對于我們這樣的專業人士可能感覺不復雜)。建造者模式最大的好處就是使得構建過程和表現分離,因此若需要改變一個產品的表現,只需要重新定義一個具體的建造者就可以了(這句話理解起來有點難度,還是拿車來打比方,我們將車的組裝過程獨立出來,用這個組裝過程,我們即可以組裝寶馬車,也可以組裝奔馳車,或者其他的車型,我們只需要重新定義一個具體的建造者(用于產品表現的類)就可以了)。
動機
在軟件系統中,有時候會遇到一個復雜對象(比如說上面例子中的汽車)的創建,它通常由幾個部分的子對象采用一定的算法(過程)構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化(比如上面例子中,各種車型用的車門、方向盤、發動機等,是不一樣的),但是將各個部分組合在一起的算法(過程)是相對穩定的。
建造者模式就是在這樣的需求下誕生的,它封裝了變化點(組成部分),使得同樣的構建過程可以創建不同的表現。
建造者模式是當在創建復雜對象的算法應該獨立于該對象的組成部分以及它們的裝配方式時適用的模式。
建造者模式包含產品類(product)、抽象建造者類(builder)、具體建造者類(concretebuilder1、concretebuilder2…)和指揮者類(director)
從下面的代碼中看各個類的使用:
personbuilder *builder = [[personthinbuilder alloc]init];
personview *personview = [persondirector creatperson:builder];
其中 personbuilder 是抽象建造者類,personthinbuilder 是具體建造者類,personview 是產品類,persondirector 是指揮者類。
由代碼了解使用方式:
1、創建具體建造者。
2、指揮者通過具體建造者來返回產品。
感覺建造者模式與工廠方法模式非常類似,但是加入了指揮者類。
結構圖
從結構圖可以看到,生成器模式有兩個重要的角色: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,只是叫法不同而已。