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

来源:https://www.cnblogs.com/zhixin9001/archive/2020/07/07/13263044.html
-Advertisement-
Play Games

什麼是建造者模式 經典建造者模式的優缺點 對建造者模式的擴展 什麼是建造者模式 建造者模式將一個複雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。創建者模式隱藏了複雜對象的創建過程,它把複雜對象的創建過程加以抽象,通過子類繼承或者重載的方式,動態的創建具有複合屬性的對象。 雖然與 ...


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

什麼是建造者模式

建造者模式將一個複雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。創建者模式隱藏了複雜對象的創建過程,它把複雜對象的創建過程加以抽象,通過子類繼承或者重載的方式,動態的創建具有複合屬性的對象。
雖然與工廠模式、抽象工廠模式、單件模式同為創建型模式,但建造者模式與之前學習的模式相比,更為關註創建過程的細節,它一般用於創建複雜對象,從獨立創建每個部分到最後的組裝,它承擔每個步驟的工作。由於它把創建每個部分都獨立為一個單一的過程,因此不僅可以完成較為精細的創建,還可以根據創建步驟編排,生成不同的目標實例。
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類圖如下:
markdown

其中包括三個角色:

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 是時候使用JSX代替createElement了 接著上面的講,當我們看到上面用createElement去實現組件,太麻煩了,別說工作效率提高了,就是那些嵌套可以嵌套正確就很贊了,所以我們需要用JSX去簡化整個邏輯。當年我做項目的時候就遇到過這樣的情況,嵌套太多,自己都快搞不明白了,在崩潰的邊緣。 ...
  • HTML——超文本標記語言 HTMl裡面有標簽,標簽又分為單標簽和雙標簽,也分為行級元素和塊級元素 標簽是用<>包裹起來的,而且必須要有<>,否則會直接顯示在瀏覽器上面哦 現在介紹一下常用標簽 我們還要區分行級元素和塊級元素,塊級元素的特點是獨占一行,可以設置寬高,行級元素不能設置寬高,如果需要設置 ...
  • 學習JSX,先瞭解一下createElement 提到JSX,不可避免的就要提到createElement,當你看完本節,你會發現,奇怪的知識又增多了。ok,我們接著上一部分繼續講。這一次的準備工作是瞭解createElement。 從Vue編譯後的代碼看createElement 你是否看過寫的V ...
  • 1.前景怎麼樣? web前端人才需求還會持續增加 據國內權威數據統計,未來五年,我國信息化人才總需求量高達1500萬—2000萬人。其中“網路工程”“UI設計”“web前端”等人才的缺口最為突出,所以2020年web前端的市場需求還是很大的。更有甚者,目前不僅大型互聯網公司擬相繼成立了專屬的web ...
  • 編寫如下的函數: function drawHexagon(x,y,L) { ctx.beginPath(); ctx.moveTo(x-sqrt3/2*L,y-L/2); ctx.lineTo(x-sqrt3/2*L,y+L/2); ctx.lineTo(x,y+L); ctx.lineTo(x+ ...
  • 作者:騰訊有數 - TabPan Taro 引入了騰訊有數的微信小程式無痕埋點能力,為 Taro 的開發者提供真·零開發的 8 大無痕埋點能力以及自定義埋點能力,包含小程式啟動、顯示、隱藏、頁面瀏覽、頁面離開、分享、下拉刷新、上拉觸底等八大自動化埋點能力以及搜索、商品歸因等定製化埋點,以及經營分析、 ...
  • 一、畫折線圖需要有如下準備如下: 1、繪製網格 2、繪製坐標系 3、繪製點 4、點連線就成了折線圖 二、繪製網格 1、確認網格(每個格子)的大小 2、確認X軸方向有多少條橫線 3、確認Y軸方向有多少條豎線 4、遍歷畫出來 <!DOCTYPE html> <html lang="en"> <head> ...
  • 上一篇 前端 瀏覽器所在客戶端信息,有瀏覽器信息後,以下是區分手機的詳細類型 雖然沒難度,但是記錄下來,方便後續無腦複製: 蘋果APP類型 1 // iOS 2 isIPhone = (userAgent: string) => /iphone/i.test(userAgent); 3 isIPod ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...