建造者模式又稱爲生成器模式,它是一種較爲複雜、使用頻率也相對較低的建立型模式。建造者模式爲客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。由於,沒有人買車會只買一個方向盤或者輪胎,你們買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給用戶,這是建造者模式須要解決的問題。設計模式
建造者模式(Builder) | 學習難度:★★★★☆ | 使用頻率:★★☆☆☆ |
M公司遊戲開發部想要開發一款名爲Manulife羣俠傳的網絡遊戲,該遊戲採用主流的RPG(角色扮演遊戲)模式,玩家能夠在遊戲中扮演各類特定的角色,而各個角色又能夠根據不一樣的遊戲情節和統計數據(如力量、魔法、技能等)具備不一樣的能力,角色也會隨着不斷升級而擁有更增強大的能力。網絡
做爲RPG遊戲的一個重要組成部分,須要對遊戲角色進行設計,並且隨着該遊戲的升級將不斷增長新的角色。不一樣類型的遊戲角色,其性別、臉型、服裝、髮型等外部特性都有所差別,例如「天使」擁有美麗的面容和披肩的長髮,並身穿一襲白裙;而「惡魔」則極其醜陋,留着光頭並穿着一件刺眼的黑衣。app
M公司決定開發一個小工具來建立遊戲角色,能夠建立不一樣類型的角色並能夠靈活地增長新角色。ide
【幾種不一樣的角色:惡魔,天使和英雄】工具
M公司的開發人員分析發現,遊戲角色是一個複雜對象,它包含性別、臉型等多個組成部分,不一樣的遊戲角色其組成部分有所差別,如上圖所示。不管是何種造型的遊戲角色,其建立步驟都大同小異,都須要逐步建立其組成部分,再將各組成部分裝配成個一個完整的遊戲角色。如何一步一步地建立一個包含多個組成部分的複雜對象,建造者模式爲解決此類問題而誕生。學習
建造者模式(Builder):將一個複雜對象的構建與它的表示相分離,使得一樣的構建過程能夠建立不一樣的表示。建造者模式是一種對象建立型模式。 測試
建造者模式是一種較爲複雜的建立型模式,他將客戶端與包含多個組成部分的複雜對象的建立過程分離,客戶端無需知道複雜對象的內部組成與裝配方式,主須要知道所需的建造者便可。其關注點在於如何一步一步地建立一個複雜對象,不一樣的具體建造者定義了不一樣的建立過程,且具體建造者相互獨立,增長新的建造者很是方便,無需修改已有代碼,系統具備較好的可擴展性。ui
從上圖能夠看出,建造者模式包含如下三類重要角色;this
(1)Builder(抽象建造者):爲建立一個產品對象的各個部件指定抽象接口,在其接口中通常包含兩類方法:一類是BuildPartX(),用於建立複雜對象的各個部件;另外一類是GetResult(),用於返回生成好的複雜對象。它就能夠是抽象類,也能夠是接口。spa
(2)ConcreteBuilder(具體建造者):實現了Builder接口,即實現了各個部件的具體構造和裝配方法,定義並明確其所建立的複雜對象。
(3)Product(產品角色):被構建的複雜對象,包含多個組成部件。
(3)Director(指揮者):負責安排複雜對象的建造次序,指揮者與抽象建造者之間存在關聯關係,能夠在其Construct()方法中調用建造者對象的部件構造和裝配方法,完成複雜對象的建造。所以,客戶端只須要和指揮者進行交互,這也確保了單一職責。
(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(); } }
建造者模式與抽象工廠模式有點類似,可是建造者模式返回一個完整的複雜產品,而抽象工廠模式則返回一系列相關的產品。若是將抽象工廠模式當作一個汽車配件生產廠,生成不一樣類型的汽車配件,那麼建造者模式則是一個汽車組裝廠,經過對配件進行組成返回一輛完整的汽車。
以建造者模式爲基礎來實現遊戲角色的設計,其中ActorController充當指揮者(Director),ActorBuilder充當抽象建造者,HeroBuilder、AngelBuilder和DevilBuilder充當具體建造者,Actor充當複雜產品。
(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; } }
調試運行結果以下:
此時若是咱們須要構建一個惡魔角色,那麼修改一下配置文件:
<add key="ConcreteBuilder" value="Manulife.ChengDu.DesignPattern.Builder.DevilBuilder, Manulife.ChengDu.DesignPattern.Builder" />
再次運行結果以下:
(1)客戶端不須要知道產品內部的組成細節,將產品自己與產品的建立過程解耦,使得相同的建立過程能夠建立不一樣的產品對象。
(2)具體建造者相對獨立,增長新的具體建造者無需修改原有類庫的代碼,系統擴展比較方便,符合開閉原則。
(3)能夠更加精細地控制產品的建立過程 -> 將複雜產品的建立步驟分解在不一樣的方法中,使得建立過程更加清晰,也更方便使用程序來控制建立過程。
(1)對於所建立的產品有必定限制:通常這些產品都具備一些較多的共同點,其組成部分類似。若是差別性很大,那麼則不適合使用建造者模式。
(2)若是產品的內部結構複雜多變,可能會須要定義不少具體構建者來實現這些變化,會致使系統變得龐大,增長系統的理解難度和運行成本。
(1)須要生成的產品對象由複雜的內部結構,這些產品對象一般包含多個成員變量。
(2)須要生成的產品對象的屬性相互依賴,須要指定其生成順序。
(3)隔離複雜對象的建立和使用,並使得相同的建立過程能夠建立不一樣的產品。
劉偉,《設計模式的藝術—軟件開發人員內功修煉之道》