- 什么是建造者模式
- 經(jīng)典建造者模式的優(yōu)缺點(diǎn)
- 對(duì)建造者模式的擴(kuò)展
什么是建造者模式
建造者模式將一個(gè)復(fù)雜的對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。創(chuàng)建者模式隱藏了復(fù)雜對(duì)象的創(chuàng)建過(guò)程,它把復(fù)雜對(duì)象的創(chuàng)建過(guò)程加以抽象,通過(guò)子類繼承或者重載的方式,動(dòng)態(tài)的創(chuàng)建具有復(fù)合屬性的對(duì)象。
雖然與工廠模式、抽象工廠模式、單件模式同為創(chuàng)建型模式,但建造者模式與之前學(xué)習(xí)的模式相比,更為關(guān)注創(chuàng)建過(guò)程的細(xì)節(jié),它一般用于創(chuàng)建復(fù)雜對(duì)象,從獨(dú)立創(chuàng)建每個(gè)部分到最后的組裝,它承擔(dān)每個(gè)步驟的工作。由于它把創(chuàng)建每個(gè)部分都獨(dú)立為一個(gè)單一的過(guò)程,因此不僅可以完成較為精細(xì)的創(chuàng)建,還可以根據(jù)創(chuàng)建步驟編排,生成不同的目標(biāo)實(shí)例。
GOF對(duì)建造者模式的描述是:
Separate the construction of a complex object from its representation so that the same construction process can create different representations..
— Design Patterns : Elements of Reusable Object-Oriented Software
創(chuàng)建者模式非常適用于產(chǎn)品局部加工過(guò)程變化較大,但組裝過(guò)程相對(duì)固定的場(chǎng)景。
比如電腦的組裝,基本的組裝過(guò)程是固定的,但是具體主板、CPU、顯卡、內(nèi)存、硬盤(pán)等選擇的品牌和型號(hào)可能差異很大;還有汽車的生產(chǎn)也是這樣,整體組裝過(guò)程基本相同,但不同品牌、價(jià)格的汽車在具體部件上差異很大。
其UML類圖如下:
其中包括三個(gè)角色:
- IBuilder:負(fù)責(zé)描述創(chuàng)建一個(gè)產(chǎn)品各個(gè)組成的抽象接口。
- Concrete Builder:實(shí)現(xiàn)IBuilder要求的內(nèi)容,并且提供一個(gè)獲得產(chǎn)品的方法。
- Director:基于IBuilder定義的構(gòu)造產(chǎn)品的抽象步驟,指導(dǎo)Concrete Builder生成產(chǎn)品的過(guò)程。
這里的產(chǎn)品類型并沒(méi)有統(tǒng)一的IProduct接口,主要是因?yàn)榻?jīng)過(guò)不同ConcreteBuilder加工后的產(chǎn)品差別相對(duì)較大,給它一個(gè)公共的基準(zhǔn)抽象對(duì)象意義不大,
代碼實(shí)現(xiàn):
public class House
{
public void AddWindowAndDoor() { }
public void AddWallAndFloor() { }
public void AddCeiling() { }
}
public class Car
{
public void AddWheel() { }
public void AddEngine() { }
public void AddBody() { }
}
public interface IBuilder
{
void BuildPart1();
void BuildPart2();
void BuildPart3();
}
public class CarBuilder : IBuilder
{
private Car car;
public void BuildPart1()
{
car.AddEngine();
}
public void BuildPart2()
{
car.AddWheel();
}
public void BuildPart3()
{
car.AddBody();
}
}
public class HouseBuilder : IBuilder
{
private House house;
public void BuildPart1()
{
house.AddWallAndFloor();
}
public void BuildPart2()
{
house.AddCeiling();
}
public void BuildPart3()
{
house.AddWindowAndDoor();
}
}
public class Director
{
public void Construct(IBuilder builder)
{
builder.BuildPart1();
builder.BuildPart2();
builder.BuildPart3();
}
}
調(diào)用:
[Test]
public void BuilderTest()
{
Director director = new Director();
CarBuilder carBuilder = new CarBuilder();
HouseBuilder houseBuilder = new HouseBuilder();
director.Construct(carBuilder);
director.Construct(houseBuilder);
Assert.AreEqual(typeof(Car), carBuilder.Car.GetType());
Assert.AreEqual(typeof(House), houseBuilder.House.GetType());
}
經(jīng)典建造者模式的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):創(chuàng)建者模式將復(fù)雜對(duì)象的每個(gè)組成創(chuàng)建步驟暴露出來(lái),借助Director(或客戶程序自己)既可以選擇其執(zhí)行次序,也可以選擇要執(zhí)行哪些步驟。上述過(guò)程可以在應(yīng)用中動(dòng)態(tài)完成,相比較工廠方法和抽象工廠模式的一次性創(chuàng)建過(guò)程而言,創(chuàng)建者模式適合創(chuàng)建“更為復(fù)雜且每個(gè)組成變化較多”的類型。
- 缺點(diǎn):但建造者模式也存在一些缺點(diǎn),比如正因?yàn)闀?huì)暴露出更多的執(zhí)行步驟,這就需要需要Director(或客戶程序)具有更多的領(lǐng)域知識(shí),使用不慎很容易造成相對(duì)更為緊密的耦合。
而且經(jīng)典建造者中IBuilder定義了數(shù)目固定的裝配動(dòng)作,而Director有把這些動(dòng)作的執(zhí)行順序也固定了,雖然建造者模式可以生產(chǎn)差異非常大的產(chǎn)品,但要求這些產(chǎn)品具有固定的裝配步驟,這就大大局限了這種模式的使用場(chǎng)景,因?yàn)楝F(xiàn)實(shí)中這樣的要求往往很難滿足。不同的產(chǎn)品往往具有數(shù)目不同的裝配動(dòng)作和次序,如果要把這樣的產(chǎn)品添加到建造者的生產(chǎn)列表中是做不到的,需要另外實(shí)現(xiàn)一套,改動(dòng)比較大。
對(duì)建造者模式的擴(kuò)展
上述問(wèn)題可以通過(guò)對(duì)經(jīng)典模式適當(dāng)優(yōu)化來(lái)解決。
IBuilder中定義的不同步驟可以進(jìn)一步抽象為一個(gè)Action,CarBuilder和HouseBuilder的代碼也很相似,可以提取一個(gè)基類,這部分代碼放在基類中。
代碼如下:
public interface IBuilder<T> where T : class, new()
{
T BuildUp();
}
public abstract class BuilderBase<T> : IBuilder<T> where T : class, new()
{
protected IList<Action> steps = new List<Action>();
protected T product = new T();
public virtual T BuildUp()
{
foreach (Action step in steps)
{
step();
}
return product;
}
}
public class ConcreteCarBuilder : BuilderBase<Car>
{
public ConcreteCarBuilder() : base()
{
steps.Add(product.AddEngine);
steps.Add(product.AddWheel);
steps.Add(product.AddBody);
}
}
public class ConcreteHouseBuilder : BuilderBase<House>
{
public ConcreteHouseBuilder() : base()
{
steps.Add(product.AddWallAndFloor);
steps.Add(product.AddCeiling);
steps.Add(product.AddWindowAndDoor);
}
}
實(shí)體Builder兼做Director,具體的構(gòu)建步驟由實(shí)體Builder來(lái)決定,這樣做的好處是非常靈活,不同的Builder可以有不同數(shù)目的動(dòng)作,動(dòng)作的順序也可以自行安排。
調(diào)用:
[Test]
public void DelegateBuilderTest()
{
IBuilder<Car> builder = new ConcreteCarBuilder();
var product = builder.BuildUp();
Assert.AreEqual(typeof(Car), product.GetType());
IBuilder<House> builder1 = new ConcreteHouseBuilder();
var product1 = builder1.BuildUp();
Assert.AreEqual(typeof(House), product1.GetType());
}
參考書(shū)籍:
王翔著 《設(shè)計(jì)模式——基于C#的工程化實(shí)現(xiàn)及擴(kuò)展》