深入解析C#設(shè)計模式編程中對建造者模式的運(yùn)用
示例
我們先來以這樣一個場景引入:
在電腦城裝機(jī)總有這樣的經(jīng)歷。我們到了店里,先會有一個銷售人員來詢問你希望裝的機(jī)器是怎么樣的配置,他會給你一些建議,最終會形成一張裝機(jī)單。和客戶確定了裝機(jī)配置以后,他會把這張單字交給提貨的人,由他來準(zhǔn)備這些配件,準(zhǔn)備完成后交給裝機(jī)技術(shù)人員。技術(shù)人員會把這些配件裝成一個整機(jī)交給客戶。
不管是什么電腦,它總是由CPU、內(nèi)存、主板、硬盤以及顯卡等部件構(gòu)成的,并且裝機(jī)的過程總是固定的:
- 把主板固定在機(jī)箱中
- 把CPU安裝到主板上
- 把內(nèi)存安裝到主板上
- 把硬盤連接到主板上
- 把顯卡安裝到主板上
但是,每臺兼容機(jī)的部件都各不相同的,有些配置高一點(diǎn),有些配置低一點(diǎn),這是變化點(diǎn)。對于裝機(jī)技術(shù)人員來說,他不需要考慮這些配件從哪里來的,他只需要把他們組裝在一起了,這是穩(wěn)定的裝機(jī)流程。要把這種變化的配件和穩(wěn)定的流程進(jìn)行分離就需要引入Builder模式。
示例代碼
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace BuilderExemple
{
classProgram
{
staticvoid Main(string[] args)
{
ComputerFactory factory = newComputerFactory();
ComputerBuilder office = newOfficeComputerBuilder();
factory.BuildComputer(office);
office.Computer.ShowSystemInfo();
ComputerBuilder game = newGameComputerBuilder();
factory.BuildComputer(game);
game.Computer.ShowSystemInfo();
}
}
classComputerFactory
{
publicvoid 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();
}
}
abstractclassComputerBuilder
{
protectedstring name;
publicstring Name
{
get { return name; }
set { name = value; }
}
protectedComputer computer;
publicComputer Computer
{
get { return computer; }
set { computer = value; }
}
public ComputerBuilder()
{
computer = newComputer();
}
publicabstractvoid SetupMainboard();
publicabstractvoid SetupCpu();
publicabstractvoid SetupMemory();
publicabstractvoid SetupHarddisk();
publicabstractvoid SetupVideocard();
}
classOfficeComputerBuilder : ComputerBuilder
{
public OfficeComputerBuilder()
{
name = "OfficeComputer";
}
publicoverridevoid SetupMainboard()
{
computer.Mainboard = "Abit升技LG-95C 主板(Intel 945GC芯片組/LGA 775/1066MHz) ";
}
publicoverridevoid SetupCpu()
{
computer.Cpu = "Intel 英特爾賽揚(yáng)D 336 (2.8GHz/LGA 775/256K/533MHz) ";
}
publicoverridevoid SetupMemory()
{
computer.Memory = "Patriot博帝DDR2 667 512MB 臺式機(jī)內(nèi)存";
}
publicoverridevoid SetupHarddisk()
{
computer.Harddisk = "Hitachi日立SATAII接口臺式機(jī)硬盤(80G/7200轉(zhuǎn)/8M)盒裝";
}
publicoverridevoid SetupVideocard()
{
computer.Videocard = "主板集成";
}
}
classGameComputerBuilder : ComputerBuilder
{
public GameComputerBuilder()
{
name = "GameComputer";
}
publicoverridevoid SetupMainboard()
{
computer.Mainboard = "GIGABYTE技嘉GA-965P-DS3 3.3 主板(INTEL P965 東莞產(chǎn))" ;
}
publicoverridevoid SetupCpu()
{
computer.Cpu = "Intel 英特爾酷睿E4400 (2.0GHz/LGA 775/2M/800MHz)盒裝";
}
publicoverridevoid SetupMemory()
{
computer.Memory = "G.SKILL 芝奇F2-6400CL5D-2GBNQ DDR2 800 1G*2臺式機(jī)內(nèi)存";
}
publicoverridevoid SetupHarddisk()
{
computer.Harddisk = "Hitachi日立SATAII接口臺式機(jī)硬盤(250G/7200轉(zhuǎn)/8M)盒裝";
}
publicoverridevoid SetupVideocard()
{
computer.Videocard = "七彩虹逸彩GT-GD3 UP烈焰戰(zhàn)神H10 顯卡(GeForce 8600GT/256M/DDR3)支持HDMI!";
}
}
classComputer
{
privatestring videocard;
publicstring Videocard
{
get { return videocard; }
set { videocard = value; }
}
privatestring cpu;
publicstring Cpu
{
get { return cpu; }
set { cpu = value; }
}
privatestring mainboard;
publicstring Mainboard
{
get { return mainboard; }
set { mainboard = value; }
}
privatestring memory;
publicstring Memory
{
get { return memory; }
set { memory = value; }
}
privatestring harddisk;
publicstring Harddisk
{
get { return harddisk; }
set { harddisk = value; }
}
publicvoid ShowSystemInfo()
{
Console.WriteLine("==================SystemInfo==================");
Console.WriteLine("CPU:" + cpu);
Console.WriteLine("MainBoard:" + mainboard);
Console.WriteLine("Memory:" + memory);
Console.WriteLine("VideoCard:" + videocard);
Console.WriteLine("HardDisk:" + harddisk);
}
}
}
代碼說明:
ComputerFactory是建造者模式的指導(dǎo)者。指導(dǎo)者做的是穩(wěn)定的建造工作,假設(shè)它就是一個技術(shù)人員,他只是在做按照固定的流程,把配件組裝成計算機(jī)的重復(fù)勞動工作。他不知道他現(xiàn)在組裝的是一臺游戲電腦還是一臺辦公用電腦,他也不知道他往主板上安裝的內(nèi)存是1G還是2G的。呵呵,看來是不稱職的技術(shù)人員。
ComputerBuilder是抽象建造者角色。它主要是用來定義兩種接口,一種接口用于規(guī)范產(chǎn)品的各個部分的組成。比如,這里就規(guī)定了組裝一臺電腦所需要的5個工序。第二種接口用于返回建造后的產(chǎn)品,在這里我們沒有定義抽象方法,反正建造出來的總是電腦。
OfficeComputerBuilder和GameComputerBuilder是具體的建造者。他的工作就是實現(xiàn)各建造步驟的接口,以及實現(xiàn)返回產(chǎn)品的接口,在這里后者省略了。
Computer就是建造出來的復(fù)雜產(chǎn)品。在代碼中,我們的各種建造步驟都是為創(chuàng)建產(chǎn)品中的各種配件服務(wù)的,Computer定義了一個相對具體的產(chǎn)品,在應(yīng)用中可以把這個產(chǎn)品進(jìn)行比較高度的抽象,使得不同的具體建造者甚至可以建造出完全不同的產(chǎn)品。
看看客戶端的代碼,用戶先是選擇了一個具體的Builder,用戶應(yīng)該很明確它需要游戲電腦還是辦公電腦,但是它可以對電腦一無所知,由銷售人員給出一個合理的配置單。然后用戶讓ComputerFactory去為它組裝這個電腦。組裝完成后ComputerFactory開機(jī),給用戶驗收電腦的配置是否正確。
你或許覺得ComputerBuilder和是抽象工廠模式中的抽象工廠角色差不多,GameComputerBuilder又像是具體工廠。其實,建造者模式和抽象工廠模式的側(cè)重點(diǎn)不同,前者強(qiáng)調(diào)一個組裝的概念,一個復(fù)雜對象由多個零件組裝而成并且組裝是按照一定的標(biāo)準(zhǔn)射順序進(jìn)行的,而后者強(qiáng)調(diào)的是創(chuàng)建一系列產(chǎn)品。建造者模式適用于組裝一臺電腦,而抽象工廠模式適用于提供用戶筆記本電腦、臺式電腦和掌上電腦的產(chǎn)品系列。
建造者模式的定義和類圖
介紹完了建造者模式的具體實現(xiàn)之后嗎,下面具體看下建造者模式的具體定義是怎樣的。
建造者模式(Builder Pattern):將一個復(fù)雜對象的構(gòu)建于它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
建造者模式使得建造代碼與表示代碼的分離,可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),從而降低了客戶端與具體產(chǎn)品之間的耦合度,下面通過類圖來幫助大家更好地理清建造者模式中類之間的關(guān)系。

建造者模式的分析
介紹完了建造者模式的具體實現(xiàn)之后,讓我們總結(jié)下建造模式的實現(xiàn)要點(diǎn):
在建造者模式中,指揮者是直接與客戶端打交道的,指揮者將客戶端創(chuàng)建產(chǎn)品的請求劃分為對各個部件的建造請求,再將這些請求委派到具體建造者角色,具體建造者角色是完成具體產(chǎn)品的構(gòu)建工作的,卻不為客戶所知道。
建造者模式主要用于“分步驟來構(gòu)建一個復(fù)雜的對象”,其中“分步驟”是一個固定的組合過程,而復(fù)雜對象的各個部分是經(jīng)常變化的(也就是說電腦的內(nèi)部組件是經(jīng)常變化的,這里指的的變化如硬盤的大小變了,CPU由單核變雙核等)。
產(chǎn)品不需要抽象類,由于建造模式的創(chuàng)建出來的最終產(chǎn)品可能差異很大,所以不大可能提煉出一個抽象產(chǎn)品類。
在前面文章中介紹的抽象工廠模式解決了“系列產(chǎn)品”的需求變化,而建造者模式解決的是 “產(chǎn)品部分” 的需要變化。
由于建造者隱藏了具體產(chǎn)品的組裝過程,所以要改變一個產(chǎn)品的內(nèi)部表示,只需要再實現(xiàn)一個具體的建造者就可以了,從而能很好地應(yīng)對產(chǎn)品組成組件的需求變化。
.NET 中建造者模式的實現(xiàn)
前面的設(shè)計模式在.NET類庫中都有相應(yīng)的實現(xiàn),那在.NET 類庫中,是否也存在建造者模式的實現(xiàn)呢? 然而對于疑問的答案是肯定的,在.NET 類庫中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一個建造者模式的實現(xiàn)。不過它的實現(xiàn)屬于建造者模式的演化,此時的建造者模式?jīng)]有指揮者角色和抽象建造者角色,StringBuilder類即扮演著具體建造者的角色,也同時扮演了指揮者和抽象建造者的角色,此時建造模式的實現(xiàn)如下:
/// <summary>
/// 建造者模式的演變
/// 省略了指揮者角色和抽象建造者角色
/// 此時具體建造者角色扮演了指揮者和建造者兩個角色
/// </summary>
public class Builder
{
// 具體建造者角色的代碼
private Product product = new Product();
public void BuildPartA()
{
product.Add("PartA");
}
public void BuildPartB()
{
product.Add("PartB");
}
public Product GetProduct()
{
return product;
}
// 指揮者角色的代碼
public void Construct()
{
BuildPartA();
BuildPartB();
}
}
/// <summary>
/// 產(chǎn)品類
/// </summary>
public class Product
{
// 產(chǎn)品組件集合
private IList<string> parts = new List<string>();
// 把單個組件添加到產(chǎn)品組件集合中
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
Console.WriteLine("產(chǎn)品開始在組裝.......");
foreach (string part in parts)
{
Console.WriteLine("組件" + part + "已裝好");
}
Console.WriteLine("產(chǎn)品組裝完成");
}
}
// 此時客戶端也要做相應(yīng)調(diào)整
class Client
{
private static Builder builder;
static void Main(string[] args)
{
builder = new Builder();
builder.Construct();
Product product = builder.GetProduct();
product.Show();
Console.Read();
}
}
StringBuilder類扮演著建造string對象的具體建造者角色,其中的ToString()方法用來返回具體產(chǎn)品給客戶端(相當(dāng)于上面代碼中GetProduct方法)。其中Append方法用來創(chuàng)建產(chǎn)品的組件(相當(dāng)于上面代碼中BuildPartA和BuildPartB方法),因為string對象中每個組件都是字符,所以也就不需要指揮者的角色的代碼(指的是Construct方法,用來調(diào)用創(chuàng)建每個組件的方法來完成整個產(chǎn)品的組裝),因為string字符串對象中每個組件都是一樣的,都是字符,所以Append方法也充當(dāng)了指揮者Construct方法的作用。
總結(jié)
到這里,建造者模式的介紹就結(jié)束了,建造者模式(Builder Pattern),將一個復(fù)雜對象的構(gòu)建與它的表示分離,使的同樣的構(gòu)建過程可以創(chuàng)建不同的表示。建造者模式的本質(zhì)是使組裝過程(用指揮者類進(jìn)行封裝,從而達(dá)到解耦的目的)和創(chuàng)建具體產(chǎn)品解耦,使我們不用去關(guān)心每個組件是如何組裝的。
相關(guān)文章
C#12中的Collection expressions集合表達(dá)式語法糖詳解
C#12中引入了新的語法糖來創(chuàng)建常見的集合,并且可以使用..來解構(gòu)集合,將其內(nèi)聯(lián)到另一個集合中,下面就跟隨小編一起學(xué)習(xí)一下C#12中這些語法糖的使用吧2023-11-11
C#獲取HTML文本的第一張圖片與截取內(nèi)容摘要示例代碼
在日常web開發(fā)的時候,經(jīng)常會遇到需要獲取保存的HTML文本中的第一張圖片,并且截取內(nèi)容摘要的效果,例如織夢的后臺添加完詳細(xì)內(nèi)容后就是自動讀取內(nèi)容摘要,并保存第一張圖片為縮略圖,那么這篇文章跟大家分享下利用C#如何實現(xiàn),感興趣的朋友們下面來一起看看吧。2016-10-10

