1. 程式人生 > >設計模式的征途—6.建造者(Builder)模式

設計模式的征途—6.建造者(Builder)模式

建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。因為,沒有人買車會只買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用者,這是建造者模式需要解決的問題。

建造者模式(Builder) 學習難度:★★★★☆ 使用頻率:★★☆☆☆

一、從遊戲角色設計談起

M公司遊戲開發部想要開發一款名為Manulife群俠傳的網路遊戲,該遊戲採用主流的RPG(角色扮演遊戲)模式,玩家可以在遊戲中扮演各種特定的角色,而各個角色又可以根據不同的遊戲情節和統計資料(如力量、魔法、技能等)具有不同的能力,角色也會隨著不斷升級而擁有更加強大的能力。

作為RPG遊戲的一個重要組成部分,需要對遊戲角色進行設計,而且隨著該遊戲的升級將不斷增加新的角色。不同型別的遊戲角色,其性別、臉型、服裝、髮型等外部特性都有所差異,例如“天使”擁有美麗的面容和披肩的長髮,並身穿一襲白裙;而“惡魔”則極其醜陋,留著光頭並穿著一件刺眼的黑衣。

M公司決定開發一個小工具來建立遊戲角色,可以建立不同型別的角色並可以靈活地增加新角色。

           

  【幾種不同的角色:惡魔,天使和英雄】

  M公司的開發人員分析發現,遊戲角色是一個複雜物件,它包含性別、臉型等多個組成部分,不同的遊戲角色其組成部分有所差異,如上圖所示。無論是何種造型的遊戲角色,其建立步驟都大同小異,都需要逐步建立其組成部分,再將各組成部分裝配成個一個完整的遊戲角色。如何一步一步地建立一個包含多個組成部分的複雜物件,建造者模式為解決此類問題而誕生。

二、建造者模式概述

2.1 建造者模式關鍵定義

建造者模式(Builder):將一個複雜物件的構建與它的表示相分離,使得同樣的構建過程可以建立不同的表示。建造者模式是一種物件建立型模式。  

  建造者模式是一種較為複雜的建立型模式,他將客戶端與包含多個組成部分的複雜物件的建立過程分離,客戶端無需知道複雜物件的內部組成與裝配方式,主需要知道所需的建造者即可。其關注點在於如何一步一步地建立一個複雜物件,不同的具體建造者定義了不同的建立過程,且具體建造者相互獨立,增加新的建造者非常方便,無需修改已有程式碼,系統具有較好的可擴充套件性。

  

  從上圖可以看出,建造者模式包含以下三類重要角色;

  (1)Builder(抽象建造者):為建立一個產品物件的各個部件指定抽象介面,在其介面中一般包含兩類方法:一類是BuildPartX(),用於建立複雜物件的各個部件;另一類是GetResult(),用於返回生成好的複雜物件。它就可以是抽象類,也可以是介面。

  (2)ConcreteBuilder(具體建造者):實現了Builder介面,即實現了各個部件的具體構造和裝配方法,定義並明確其所建立的複雜物件。

  (3)Product(產品角色):被構建的複雜物件,包含多個組成部件。

  (3)Director(指揮者):負責安排複雜物件的建造次序,指揮者與抽象建造者之間存在關聯關係,可以在其Construct()方法中呼叫建造者物件的部件構造和裝配方法,完成複雜物件的建造。因此,客戶端只需要和指揮者進行互動,這也確保了單一職責。

2.2 建造者模式典型實現

  (1)複雜物件:包含多個成員變數的物件,這些成員也稱為部件或零件。一個典型的複雜物件類如下:

    public class Product
    {
        public string PartA { get; set; }
        public string PartB { get; set; }
        public string PartC { get; set; }
    }

  (2)抽象建造者:定義了產品的建立和返回方法。

    public abstract class Builder
    {
        // 建立產品物件
        protected Product product = new Product();

        public abstract void BuildPartA();
        public abstract void BuildPartB();
        public abstract void BuildPartC();

        // 返回產品物件
        public Product GetResult()
        {
            return product;
        }
    }

  (3)指揮者物件:控制整個產品的建立過程。

    public class Director
    {
        private Builder builder;

        public Director(Builder builder)
        {
            this.builder = builder;
        }

        public void SetBuilder(Builder builder)
        {
            this.builder = builder;
        }

        // 產品構建與組裝方法
        public Product Construct()
        {
            builder.BuildPartA();
            builder.BuildPartB();
            builder.BuildPartC();

            return builder.GetResult();
        }
    }

2.3 建造者模式與抽象工廠模式的對比

  建造者模式與抽象工廠模式有點相似,但是建造者模式返回一個完整的複雜產品,而抽象工廠模式則返回一系列相關的產品。如果將抽象工廠模式看成一個汽車配件生產廠,生成不同型別的汽車配件,那麼建造者模式則是一個汽車組裝廠,通過對配件進行組成返回一輛完整的汽車

三、遊戲角色設計實現方案

3.1 方案結構圖

  以建造者模式為基礎來實現遊戲角色的設計,其中ActorController充當指揮者(Director),ActorBuilder充當抽象建造者,HeroBuilder、AngelBuilder和DevilBuilder充當具體建造者,Actor充當複雜產品。

3.2 具體實現

  (1)Actor:複雜產品

    /// <summary>
    /// Actor 角色類 : 複雜產品,這裡只列出部分成員變數
    /// </summary>
    public class Actor
    {
        // 角色型別
        public string Type { get; set; }
        // 性別
        public string Sex { get; set; }
        // 臉型
        public string Face { get; set; }
        // 服裝
        public string Costume { get; set; }
        // 髮型
        public string HairStyle { get; set; }
    }

  (2)ActorBuilder:抽象建造者

    /// <summary>
    /// 角色建造器 : 抽象建造者
    /// </summary>
    public abstract class ActorBuilder
    {
        protected Actor actor = new Actor();

        public abstract void BuildType();
        public abstract void BuildSex();
        public abstract void BuildFace();
        public abstract void BuildCostume();
        public abstract void BuildHairStyle();

        // 工廠方法 : 返回一個完整的遊戲角色物件
        public Actor CreateActor()
        {
            return actor;
        }
    }

  (3)HeroBuilder、AngelBuilder和DevilBuilder:具體建造者

    /// <summary>
    /// 天使角色建造器 :具體建造者
    /// </summary>
    public class AngelBuilder : ActorBuilder
    {
        public override void BuildCostume()
        {
            actor.Costume = "白裙";
        }

        public override void BuildFace()
        {
            actor.Face = "漂亮";
        }

        public override void BuildHairStyle()
        {
            actor.HairStyle = "披肩長髮";
        }

        public override void BuildSex()
        {
            actor.Sex = "";
        }

        public override void BuildType()
        {
            actor.Type = "天使";
        }
    }

    /// <summary>
    /// 惡魔角色建造器 :具體建造者
    /// </summary>
    public class DevilBuilder : ActorBuilder
    {
        public override void BuildCostume()
        {
            actor.Costume = "黑衣";
        }

        public override void BuildFace()
        {
            actor.Face = "醜陋";
        }

        public override void BuildHairStyle()
        {
            actor.HairStyle = "光頭";
        }

        public override void BuildSex()
        {
            actor.Sex = "";
        }

        public override void BuildType()
        {
            actor.Type = "惡魔";
        }
    }

    /// <summary>
    /// 英雄建造器 : 具體建造者
    /// </summary>
    public class HeroBuilder : ActorBuilder
    {
        public override void BuildCostume()
        {
            actor.Costume = "盔甲";
        }

        public override void BuildFace()
        {
            actor.Face = "英俊";
        }

        public override void BuildHairStyle()
        {
            actor.HairStyle = "飄逸";
        }

        public override void BuildSex()
        {
            actor.Sex = "";
        }

        public override void BuildType()
        {
            actor.Type = "英雄";
        }
    }

  (4)ActorController:指揮者

    /// <summary>
    /// 遊戲角色建立控制器:指揮者(Director)
    /// </summary>
    public class ActorController
    {
        /// <summary>
        /// 逐步構建複雜產品物件
        /// </summary>
        public Actor Construct(ActorBuilder builder)
        {
            builder.BuildType();
            builder.BuildSex();
            builder.BuildFace();
            builder.BuildCostume();
            builder.BuildHairStyle();

            return builder.CreateActor(); ;
        }
    }

  (5)客戶端測試

    public class Client
    {
        public static void Main(string[] args)
        {
            ActorBuilder builder = (ActorBuilder)AppConfigHelper.GetConcreteBuilderInstance();
            ActorController director = new ActorController();
            Actor actor = director.Construct(builder);

            Console.WriteLine("角色型別:{0}", actor.Type);
            Console.WriteLine("角色性別:{0}", actor.Sex);
            Console.WriteLine("角色面容:{0}", actor.Face);
            Console.WriteLine("角色服裝:{0}", actor.Costume);
            Console.WriteLine("角色髮型:{0}", actor.HairStyle);

            Console.ReadKey();
        }
    }

  這裡仍然採用了基於配置檔案的方式:將具體的構建者配置在XML檔案中,如需要更改只需要更改一下配置檔案即可。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="ConcreteBuilder" value="Manulife.ChengDu.DesignPattern.Builder.HeroBuilder, Manulife.ChengDu.DesignPattern.Builder" />
  </appSettings>
</configuration>

  其中AppConfigHelper類的程式碼如下:

    public class AppConfigHelper
    {
        public static string GetConcreteBuilderName()
        {
            string factoryName = null;
            try
            {
                factoryName = System.Configuration.ConfigurationManager.AppSettings["ConcreteBuilder"];
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return factoryName;
        }

        public static object GetConcreteBuilderInstance()
        {
            string assemblyName = AppConfigHelper.GetConcreteBuilderName();
            Type type = Type.GetType(assemblyName);

            var instance = Activator.CreateInstance(type);
            return instance;
        }
    }
View Code

  除錯執行結果如下:

  

  此時如果我們需要構建一個惡魔角色,那麼修改一下配置檔案:

<add key="ConcreteBuilder" value="Manulife.ChengDu.DesignPattern.Builder.DevilBuilder, Manulife.ChengDu.DesignPattern.Builder" />

  再次執行結果如下:

  

四、建造者模式小結

4.1 主要優點

  (1)客戶端不需要知道產品內部的組成細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。

  (2)具體建造者相對獨立,增加新的具體建造者無需修改原有類庫的程式碼,系統擴充套件比較方便,符合開閉原則。

  (3)可以更加精細地控制產品的建立過程 -> 將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。

4.2 主要缺點

  (1)對於所建立的產品有一定限制:一般這些產品都具有一些較多的共同點,其組成部分相似。如果差異性很大,那麼則不適合使用建造者模式。

  (2)如果產品的內部結構複雜多變,可能會需要定義很多具體構建者來實現這些變化,會導致系統變得龐大,增加系統的理解難度和執行成本。

4.3 應用場景

  (1)需要生成的產品物件由複雜的內部結構,這些產品物件通常包含多個成員變數。

  (2)需要生成的產品物件的屬性相互依賴,需要指定其生成順序。

  (3)隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。

參考資料

      DesignPattern

  劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

設計模式征途6.建造Builder模式

建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。因為,沒有人買車會只買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用

【java設計模式】之 建造Builder模式

        我們還是舉上一節的例子:生產汽車。上一節我們通過模板方法模式控制汽車跑起來的動作,那麼需求是無止境的,現在如果老闆又增加了額外的需求:汽車啟動、停止、鳴笛引擎聲都由客戶自己控制,他想要什麼順序就什麼順序,那該如何做呢? 1. 汽車無休止的改造       

設計模式-建造Builder

sequence intern value 技術分享 模式 mar pen ear nds 2018-1-18 by Atlas 應用場景 需要采取循序漸進組合復雜對象時。 UML 用過elasticsearch java api的小夥伴,自然了解Bool

設計模式3—— 建立型——建造Builder

說明 在眾多開源框架或者jdk原始碼中常常出現Builder,build相關的類檔名或者類名,函式名。其中很多如此命名的原因就是因為使用了建造者(Builder)模式。檢視jdk原始碼不難發現,我們常用的StringBuilder類也使用了建造者模式。 建造者模式介

設計模式3—— 建造Builder

說明 在眾多開源框架或者jdk原始碼中常常出現Builder,build相關的類檔名或者類名,函式名。其中很多如此命名的原因就是因為使用了建造者(Builder)模式。檢視jdk原始碼不難發現,我們常

設計模式征途—22.中介Mediator模式

我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者傳送相同的資訊和檔案,從而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通訊,使用者與使用者之間的聯絡可以通過群的機制來實現。 在有些軟體中,某些類/物件之間的相互呼叫關係錯綜複

設計模式征途—15.觀察Observer模式

在日常生活中,交通訊號燈指揮者日益擁擠的城市交通。紅燈亮,汽車停止;綠燈亮,汽車繼續前行;在這個過程中,交通訊號燈是汽車的觀察目標,而汽車則是觀察者。隨著交通訊號燈的變化,汽車的行為也會隨之變化,一盞交通訊號燈可以指揮多輛汽車。在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的狀態或行

路一步步走>> 設計模式三:Builder-建造生成器

package com.test.DPs.ChuangJian.Builder; /** * 建立型:Builder-建造者(生成器) */ public class Builder{ static class Student{ String name = null; Strin

設計模式征途—23.解釋器Interpreter模式

args 參考資料 轉載 返回 下一個 tle title 缺點 images 雖然目前計算機編程語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或文件,就能按照預定的文法規則來對句子或文件進行解釋。例如,我們想要只輸入一個加法

設計模式:觀察Observer模式

image 強制轉換 trace vat PE sta obs observer -a 設計模式:觀察者(Observer)模式 一、前言 觀察者模式其實最好的名稱應該是“發布訂閱”模式,和我們現在大數據之中的發布訂閱方式比較類似,但是也有區別的地方,在上一個設計模式,

設計模式之裝飾Decorator模式

首先來看一個場景,如圖: 工人分為很多種類,比如電工,管道工等等,同時又有A公司的電工,B公司的電工,A公司的管道工,B公司的管道工等等,那麼當有M個工種和N個公司的時候,就會有 M * N 個子類,這個繼承體系就會變得很龐大和複雜。那麼如何簡化呢,那麼

設計模式征途—1.單例Singleton模式

  單例模式屬於建立型模式的一種,建立型模式是一類最常用的設計模式,在軟體開發中應用非常廣泛。建立型模式將物件的建立和使用分離,在使用物件時無需關心物件的建立細節,從而降低系統的耦合度,讓設計方案更易於修改和擴充套件。每一個建立型模式都在檢視回答3個問題:3W -> 建立什麼(What)、由誰建立(Wh

設計模式征途—8.橋接Bridge模式

在現實生活中,我們常常會用到兩種或多種型別的筆,比如毛筆和蠟筆。假設我們需要大、中、小三種類型的畫筆來繪製12中不同的顏色,如果我們使用蠟筆,需要準備3*12=36支。但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及的物件個數僅為3+12=15,遠遠小於36卻能實現與36支蠟筆同樣的功

設計模式征途—12.享元Flyweight模式

現在在大力推行節約型社會,“浪費可恥,節儉光榮”。在軟體系統中,有時候也會存在資源浪費的情況,例如,在計算機記憶體中儲存了多個完全相同或者非常相似的物件,如果這些物件的數量太多將導致系統執行代價過高。那麼,是否存在一種技術可以用於節約記憶體使用空間,實現對這些相同或者相似物件的共享訪問呢?答案是肯定的,這種技

設計模式】module模式&&Revealing module 揭示模式

但是 出版 參數傳遞 9.png 自然 指向 們的 private 初級 寫在前面 《head first設計模式》裏有一篇文章,是說使用模式的心智,   1、初學者"心智" :"我要為HELLO WORLD找個模式"   2、中級人員模式: "或許這裏我需要一個單件

《Java多執行緒程式設計實戰》——第6章 Promise承諾模式

Promise模式是一種非同步程式設計模式。 開始一個任務的執行,並得到一個用於獲取該任務執行結果的憑據物件,而不必等待該任務執行完畢就可以繼續執行其他操作。 等到需要該任務的執行結果時,再呼叫憑據物件的相關方法來獲取。 類圖 獲取執行結果時,可能由於非同步任務尚未執行完畢而阻塞。

【java設計模式】之 單例Singleton模式

1. 單例模式的定義         單例模式(Singleton Pattern)是一個比較簡單的模式,其原始定義如下:Ensure a class has only one instance, and provide a global point of access

構建器Builder模式

一、引言 在日常的開發中,我們可能經常能看到如下的程式碼: PrivateKeyDecryptParam param = new PrivateKeyDecryptParam.Builder()

程式設計模式(十二) C++ 代理Proxy模式

2.7 Proxy 代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況:        1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不

設計模式建造模式Builder

人的 做出 字體 存在 分享 定義 固定 也不會 抽象方法 一個人活到70歲以上,都會經歷這樣的幾個階段:嬰兒,少年,青年,中年,老年。並且每個人在各個階段肯定是不一樣的呀,我覺得可以說世界上不存在兩個人在人生的這5個階段的生活完全一樣,但是活到70歲以上的人,都經歷了這幾