意圖
將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。
場景
在電腦城裝機總有這樣的經歷。我們到了店裡,先會有一個銷售人員來詢問你希望裝的機器是怎麼樣的配置,他會給你一些建議,最終會形成一張裝機單。和客戶確定了裝機配置以後,他會把這張單字交給提貨的人,由他來准備這些配件,准備完成後交給裝機技術人員。技術人員會把這些配件裝成一個整機交給客戶。
不管是什麼電腦,它總是由CPU、內存、主板、硬盤以及顯卡等部件構成的,並且裝機的過程總是固定的:
l 把主板固定在機箱中
l 把CPU安裝到主板上
l 把內存安裝到主板上
l 把硬盤連接到主板上
l 把顯卡安裝到主板上
但是,每台兼容機的部件都各不相同的,有些配置高一點,有些配置低一點,這是變化點。對於裝機技術人員來說,他不需要考慮這些配件從哪裡來的,他只需要把他們組裝在一起了,這是穩定的裝機流程。要把這種變化的配件和穩定的流程進行分離就需要引入Builder模式。
示例代碼
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace BuilderExemple
{
class Program
{
static void Main(string[] args)
{
ComputerFactory factory = new ComputerFactory();
ComputerBuilder office = new OfficeComputerBuilder();
factory.BuildComputer(office);
office.Computer.ShowSystemInfo();
ComputerBuilder game = new GameComputerBuilder();
factory.BuildComputer(game);
game.Computer.ShowSystemInfo();
}
}
class ComputerFactory
{
public void BuildComputer(ComputerBuilder cb)
{
Console.WriteLine();
Console.WriteLine(">>>>>>>>>>>>>>>>>>Start Building " + cb.Name);
cb.SetupMainboard();
cb.SetupCpu();
cb.SetupMemory();
cb.SetupHarddisk();
cb.SetupVideocard();
Console.WriteLine(">>>>>>>>>>>>>>>>>>Build " + cb.Name + " Completed");
Console.WriteLine();
}
}
abstract class ComputerBuilder
{
protected string name;
public string Name
{
get { return name; }
set { name = value; }
}
protected Computer computer;
public Computer Computer
{
get { return computer; }
set { computer = value; }
}
public ComputerBuilder()
{
computer = new Computer();
}
public abstract void SetupMainboard();
public abstract void SetupCpu();
public abstract void SetupMemory();
public abstract void SetupHarddisk();
public abstract void SetupVideocard();
}
class OfficeComputerBuilder : ComputerBuilder
{
public OfficeComputerBuilder()
{
name = "OfficeComputer";
}
public override void SetupMainboard()
{
computer.Mainboard = "Abit升技LG-95C 主板(Intel 945GC芯片組/LGA 775/1066MHz) ";
}
public override void SetupCpu()
{
computer.Cpu = "Intel 英特爾賽揚D 336 (2.8GHz/LGA 775/256K/533MHz) ";
}
public override void SetupMemory()
{
computer.Memory = "Patriot博帝DDR2 667 512MB 台式機內存";
}
public override void SetupHarddisk()
{
computer.Harddisk = "Hitachi日立SATAII接口台式機硬盤(80G/7200轉/8M)盒裝";
}
public override void SetupVideocard()
{
computer.Videocard = "主板集成";
}
}
class GameComputerBuilder : ComputerBuilder
{
public GameComputerBuilder()
{
name = "GameComputer";
}
public override void SetupMainboard()
{
computer.Mainboard = "GIGABYTE技嘉GA-965P-DS3 3.3 主板(INTEl P965 東莞產)" ;
}
public override void SetupCpu()
{
computer.Cpu = "Intel 英特爾酷睿E4400 (2.0GHz/LGA 775/2M/800MHz)盒裝";
}
public override void SetupMemory()
{
computer.Memory = "G.SKILl 芝奇F2-6400CL5D-2GBNQ DDR2 800 1G*2台式機內存";
}
public override void SetupHarddisk()
{
computer.Harddisk = "Hitachi日立SATAII接口台式機硬盤(250G/7200轉/8M)盒裝";
}
public override void SetupVideocard()
{
computer.Videocard = "七彩虹逸彩GT-GD3 UP烈焰戰神H10 顯卡(GeForce 8600GT/256M/DDR3)支持HDMI!";
}
}
class Computer
{
private string videocard;
public string Videocard
{
get { return videocard; }
set { videocard = value; }
}
private string cpu;
public string Cpu
{
get { return cpu; }
set { cpu = value; }
}
private string mainboard;
public string Mainboard
{
get { return mainboard; }
set { mainboard = value; }
}
private string memory;
public string Memory
{
get { return memory; }
set { memory = value; }
}
private string harddisk;
public string Harddisk
{
get { return harddisk; }
set { harddisk = value; }
}
public void ShowSystemInfo()
{
Console.WriteLine("==================SystemInfo==================");
Console.WriteLine("CPU:" + cpu);
Console.WriteLine("MainBoard:" + mainboard);
Console.WriteLine("Memory:" + memory);
Console.WriteLine("VideoCard:" + videocard);
Console.WriteLine("HardDisk:" + harddisk);
}
}
}
代碼執行結果如下圖:
代碼說明
l ComputerFactory是建造者模式的指導者。指導者做的是穩定的建造工作,假設它就是一個技術人員,他只是在做按照固定的流程,把配件組裝成計算機的重復勞動工作。他不知道他現在組裝的是一台游戲電腦還是一台辦公用電腦,他也不知道他往主板上安裝的內存是1G還是2G的。呵呵,看來是不稱職的技術人員。
l ComputerBuilder是抽象建造者角色。它主要是用來定義兩種接口,一種接口用於規范產品的各個部分的組成。比如,這裡就規定了組裝一台電腦所需要的5個工序。第二種接口用於返回建造後的產品,在這裡我們沒有定義抽象方法,反正建造出來的總是電腦。
l OfficeComputerBuilder和GameComputerBuilder是具體的建造者。他的工作就是實現各建造步驟的接口,以及實現返回產品的接口,在這裡後者省略了。
l Computer就是建造出來的復雜產品。在代碼中,我們的各種建造步驟都是為創建產品中的各種配件服務的,Computer定義了一個相對具體的產品,在應用中可以把這個產品進行比較高度的抽象,使得不同的具體建造者甚至可以建造出完全不同的產品。
l 看看客戶端的代碼,用戶先是選擇了一個具體的Builder,用戶應該很明確它需要游戲電腦還是辦公電腦,但是它可以對電腦一無所知,由銷售人員給出一個合理的配置單。然後用戶讓ComputerFactory去為它組裝這個電腦。組裝完成後ComputerFactory開機,給用戶驗收電腦的配置是否正確。
l 你或許覺得ComputerBuilder和是抽象工廠模式中的抽象工廠角色差不多,GameComputerBuilder又像是具體工廠。其實,建造者模式和抽象工廠模式的側重點不同,前者強調一個組裝的概念,一個復雜對象由多個零件組裝而成並且組裝是按照一定的標准射順序進行的,而後者強調的是創建一系列產品。建造者模式適用於組裝一台電腦,而抽象工廠模式適用於提供用戶筆記本電腦、台式電腦和掌上電腦的產品系列。
何時采用
l 從代碼角度來說, 如果你希望分離復雜類型構建規則和類型內部組成,或者希望把相同的構建過程用於構建不同類型的時候可以考慮使用建造者模式。
l 從應用角度來說, 如果你希望解耦產品的創建過程和產品的具體配件,或者你希望為所有產品的創建復用一套穩定並且復雜的邏輯的時候可以考慮使用建造者模式。
實現要點
l 對象的構建過程由指導者完成,具體的組成由具體建造者完成,表示與構建分離。
l 建造者和指導者是建造者模式的關鍵點,如果進行合並或省略就可能會轉變到模版方法模式。
l 如果對象的建造步驟是簡單的,並且產品擁有一致的接口可以轉而使用工廠模式。
注意事項
l 返回產品的方法是否必須,是否一定要在抽象建造者中有接口根據實際情況而定。如果它們有統一的接口可以在抽象建造者中體現這個抽象方法,如果沒有統一的接口(比如,生產毫無關聯的產品)則可以在具體建造者中各自實現這個方法,如果創建的產品是一種產品,那麼甚至可以省略返回產品的接口(本文的例子就是這樣)。