什麼是建造者模式 經典建造者模式的優缺點 對建造者模式的擴展 什麼是建造者模式 建造者模式將一個複雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。創建者模式隱藏了複雜對象的創建過程,它把複雜對象的創建過程加以抽象,通過子類繼承或者重載的方式,動態的創建具有複合屬性的對象。 雖然與 ...
- 什麼是建造者模式
- 經典建造者模式的優缺點
- 對建造者模式的擴展
什麼是建造者模式
建造者模式將一個複雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。創建者模式隱藏了複雜對象的創建過程,它把複雜對象的創建過程加以抽象,通過子類繼承或者重載的方式,動態的創建具有複合屬性的對象。
雖然與工廠模式、抽象工廠模式、單件模式同為創建型模式,但建造者模式與之前學習的模式相比,更為關註創建過程的細節,它一般用於創建複雜對象,從獨立創建每個部分到最後的組裝,它承擔每個步驟的工作。由於它把創建每個部分都獨立為一個單一的過程,因此不僅可以完成較為精細的創建,還可以根據創建步驟編排,生成不同的目標實例。
GOF對建造者模式的描述是:
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
創建者模式非常適用於產品局部加工過程變化較大,但組裝過程相對固定的場景。
比如電腦的組裝,基本的組裝過程是固定的,但是具體主板、CPU、顯卡、記憶體、硬碟等選擇的品牌和型號可能差異很大;還有汽車的生產也是這樣,整體組裝過程基本相同,但不同品牌、價格的汽車在具體部件上差異很大。
其UML類圖如下:
其中包括三個角色:
- IBuilder:負責描述創建一個產品各個組成的抽象介面。
- Concrete Builder:實現IBuilder要求的內容,並且提供一個獲得產品的方法。
- Director:基於IBuilder定義的構造產品的抽象步驟,指導Concrete Builder生成產品的過程。
這裡的產品類型並沒有統一的IProduct介面,主要是因為經過不同ConcreteBuilder加工後的產品差別相對較大,給它一個公共的基準抽象對象意義不大,
代碼實現:
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();
}
}
調用:
[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());
}
經典建造者模式的優缺點
- 優點:創建者模式將複雜對象的每個組成創建步驟暴露出來,藉助Director(或客戶程式自己)既可以選擇其執行次序,也可以選擇要執行哪些步驟。上述過程可以在應用中動態完成,相比較工廠方法和抽象工廠模式的一次性創建過程而言,創建者模式適合創建“更為複雜且每個組成變化較多”的類型。
- 缺點:但建造者模式也存在一些缺點,比如正因為會暴露出更多的執行步驟,這就需要需要Director(或客戶程式)具有更多的領域知識,使用不慎很容易造成相對更為緊密的耦合。
而且經典建造者中IBuilder定義了數目固定的裝配動作,而Director有把這些動作的執行順序也固定了,雖然建造者模式可以生產差異非常大的產品,但要求這些產品具有固定的裝配步驟,這就大大局限了這種模式的使用場景,因為現實中這樣的要求往往很難滿足。不同的產品往往具有數目不同的裝配動作和次序,如果要把這樣的產品添加到建造者的生產列表中是做不到的,需要另外實現一套,改動比較大。
對建造者模式的擴展
上述問題可以通過對經典模式適當優化來解決。
IBuilder中定義的不同步驟可以進一步抽象為一個Action,CarBuilder和HouseBuilder的代碼也很相似,可以提取一個基類,這部分代碼放在基類中。
代碼如下:
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);
}
}
實體Builder兼做Director,具體的構建步驟由實體Builder來決定,這樣做的好處是非常靈活,不同的Builder可以有不同數目的動作,動作的順序也可以自行安排。
調用:
[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());
}
參考書籍:
王翔著 《設計模式——基於C#的工程化實現及擴展》