設計模式(4) 建造者模式

  • 什麼是建造者模式
  • 經典建造者模式的優缺點
  • 對建造者模式的擴展

什麼是建造者模式

建造者模式將一個複雜的對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。建立者模式隱藏了複雜對象的建立過程,它把複雜對象的建立過程加以抽象,經過子類繼承或者重載的方式,動態的建立具備複合屬性的對象。
雖然與工廠模式、抽象工廠模式、單件模式同爲建立型模式,但建造者模式與以前學習的模式相比,更爲關注建立過程的細節,它通常用於建立複雜對象,從獨立建立每一個部分到最後的組裝,它承擔每一個步驟的工做。因爲它把建立每一個部分都獨立爲一個單一的過程,所以不只能夠完成較爲精細的建立,還能夠根據建立步驟編排,生成不一樣的目標實例。
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、顯卡、內存、硬盤等選擇的品牌和型號可能差別很大;還有汽車的生產也是這樣,總體組裝過程基本相同,但不一樣品牌、價格的汽車在具體部件上差別很大。markdown

其UML類圖以下:
markdown學習

其中包括三個角色:優化

  • IBuilder:負責描述建立一個產品各個組成的抽象接口。
  • Concrete Builder:實現IBuilder要求的內容,而且提供一個得到產品的方法。
  • Director:基於IBuilder定義的構造產品的抽象步驟,指導Concrete Builder生成產品的過程。

這裏的產品類型並無統一的IProduct接口,主要是由於通過不一樣ConcreteBuilder加工後的產品差異相對較大,給它一個公共的基準抽象對象意義不大,ui

代碼實現:設計

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();
    }
}

調用:3d

[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有把這些動做的執行順序也固定了,雖然建造者模式能夠生產差別很是大的產品,但要求這些產品具備固定的裝配步驟,這就大大侷限了這種模式的使用場景,由於現實中這樣的要求每每很難知足。不一樣的產品每每具備數目不一樣的裝配動做和次序,若是要把這樣的產品添加到建造者的生產列表中是作不到的,須要另外實現一套,改動比較大。code

對建造者模式的擴展

上述問題能夠經過對經典模式適當優化來解決。
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能夠有不一樣數目的動做,動做的順序也能夠自行安排。blog

調用:

[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#的工程化實現及擴展》

相關文章
相關標籤/搜索