
建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為用戶端傳回的不是一個簡單的産品,而是一個由多個部件組成的複雜産品。因為,沒有人買車會隻買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何将這些部件組裝成一輛完整的汽車并傳回給使用者,這是建造者模式需要解決的問題。
建造者模式(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)隔離複雜對象的建立和使用,并使得相同的建立過程可以建立不同的産品。
參考資料
劉偉,《設計模式的藝術—軟體開發人員内功修煉之道》
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。