深入解析C#設計模式編程中對建造者模式的運用
示例
我們先來以這樣一個場景引入: 
在電腦城裝機總有這樣的經歷。我們到了店里,先會有一個銷售人員來詢問你希望裝的機器是怎么樣的配置,他會給你一些建議,最終會形成一張裝機單。和客戶確定了裝機配置以后,他會把這張單字交給提貨的人,由他來準備這些配件,準備完成后交給裝機技術人員。技術人員會把這些配件裝成一個整機交給客戶。
不管是什么電腦,它總是由CPU、內存、主板、硬盤以及顯卡等部件構成的,并且裝機的過程總是固定的:
- 把主板固定在機箱中
 - 把CPU安裝到主板上
 - 把內存安裝到主板上
 - 把硬盤連接到主板上
 - 把顯卡安裝到主板上
 
但是,每臺兼容機的部件都各不相同的,有些配置高一點,有些配置低一點,這是變化點。對于裝機技術人員來說,他不需要考慮這些配件從哪里來的,他只需要把他們組裝在一起了,這是穩(wěn)定的裝機流程。要把這種變化的配件和穩(wě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 英特爾賽揚D 336 (2.8GHz/LGA 775/256K/533MHz) ";
    }
 
    publicoverridevoid SetupMemory()
    {
      computer.Memory = "Patriot博帝DDR2 667 512MB 臺式機內存";
    }
 
    publicoverridevoid SetupHarddisk()
    {
      computer.Harddisk = "Hitachi日立SATAII接口臺式機硬盤(80G/7200轉/8M)盒裝";
    }
 
    publicoverridevoid SetupVideocard()
    {
      computer.Videocard = "主板集成";
    }
  }
 
  classGameComputerBuilder : ComputerBuilder
  {
    public GameComputerBuilder()
    {
      name = "GameComputer";
    }
 
    publicoverridevoid SetupMainboard()
    {
      computer.Mainboard = "GIGABYTE技嘉GA-965P-DS3 3.3 主板(INTEL P965 東莞產)" ;
    }
 
    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臺式機內存";
    }
 
    publicoverridevoid SetupHarddisk()
    {
      computer.Harddisk = "Hitachi日立SATAII接口臺式機硬盤(250G/7200轉/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是建造者模式的指導者。指導者做的是穩(wěn)定的建造工作,假設它就是一個技術人員,他只是在做按照固定的流程,把配件組裝成計算機的重復勞動工作。他不知道他現在組裝的是一臺游戲電腦還是一臺辦公用電腦,他也不知道他往主板上安裝的內存是1G還是2G的。呵呵,看來是不稱職的技術人員。
ComputerBuilder是抽象建造者角色。它主要是用來定義兩種接口,一種接口用于規(guī)范產品的各個部分的組成。比如,這里就規(guī)定了組裝一臺電腦所需要的5個工序。第二種接口用于返回建造后的產品,在這里我們沒有定義抽象方法,反正建造出來的總是電腦。
OfficeComputerBuilder和GameComputerBuilder是具體的建造者。他的工作就是實現各建造步驟的接口,以及實現返回產品的接口,在這里后者省略了。
Computer就是建造出來的復雜產品。在代碼中,我們的各種建造步驟都是為創(chuàng)建產品中的各種配件服務的,Computer定義了一個相對具體的產品,在應用中可以把這個產品進行比較高度的抽象,使得不同的具體建造者甚至可以建造出完全不同的產品。
看看客戶端的代碼,用戶先是選擇了一個具體的Builder,用戶應該很明確它需要游戲電腦還是辦公電腦,但是它可以對電腦一無所知,由銷售人員給出一個合理的配置單。然后用戶讓ComputerFactory去為它組裝這個電腦。組裝完成后ComputerFactory開機,給用戶驗收電腦的配置是否正確。
你或許覺得ComputerBuilder和是抽象工廠模式中的抽象工廠角色差不多,GameComputerBuilder又像是具體工廠。其實,建造者模式和抽象工廠模式的側重點不同,前者強調一個組裝的概念,一個復雜對象由多個零件組裝而成并且組裝是按照一定的標準射順序進行的,而后者強調的是創(chuàng)建一系列產品。建造者模式適用于組裝一臺電腦,而抽象工廠模式適用于提供用戶筆記本電腦、臺式電腦和掌上電腦的產品系列。
建造者模式的定義和類圖
  介紹完了建造者模式的具體實現之后嗎,下面具體看下建造者模式的具體定義是怎樣的。
建造者模式(Builder Pattern):將一個復雜對象的構建于它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。
建造者模式使得建造代碼與表示代碼的分離,可以使客戶端不必知道產品內部組成的細節(jié),從而降低了客戶端與具體產品之間的耦合度,下面通過類圖來幫助大家更好地理清建造者模式中類之間的關系。
建造者模式的分析
介紹完了建造者模式的具體實現之后,讓我們總結下建造模式的實現要點:
在建造者模式中,指揮者是直接與客戶端打交道的,指揮者將客戶端創(chuàng)建產品的請求劃分為對各個部件的建造請求,再將這些請求委派到具體建造者角色,具體建造者角色是完成具體產品的構建工作的,卻不為客戶所知道。
建造者模式主要用于“分步驟來構建一個復雜的對象”,其中“分步驟”是一個固定的組合過程,而復雜對象的各個部分是經常變化的(也就是說電腦的內部組件是經常變化的,這里指的的變化如硬盤的大小變了,CPU由單核變雙核等)。
產品不需要抽象類,由于建造模式的創(chuàng)建出來的最終產品可能差異很大,所以不大可能提煉出一個抽象產品類。
在前面文章中介紹的抽象工廠模式解決了“系列產品”的需求變化,而建造者模式解決的是 “產品部分” 的需要變化。
由于建造者隱藏了具體產品的組裝過程,所以要改變一個產品的內部表示,只需要再實現一個具體的建造者就可以了,從而能很好地應對產品組成組件的需求變化。
.NET 中建造者模式的實現
  前面的設計模式在.NET類庫中都有相應的實現,那在.NET 類庫中,是否也存在建造者模式的實現呢? 然而對于疑問的答案是肯定的,在.NET 類庫中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一個建造者模式的實現。不過它的實現屬于建造者模式的演化,此時的建造者模式沒有指揮者角色和抽象建造者角色,StringBuilder類即扮演著具體建造者的角色,也同時扮演了指揮者和抽象建造者的角色,此時建造模式的實現如下:
/// <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>
  /// 產品類
  /// </summary>
  public class Product
  {
    // 產品組件集合
    private IList<string> parts = new List<string>();
    // 把單個組件添加到產品組件集合中
    public void Add(string part)
    {
      parts.Add(part);
    }
    public void Show()
    {
      Console.WriteLine("產品開始在組裝.......");
      foreach (string part in parts)
      {
        Console.WriteLine("組件" + part + "已裝好");
      }
      Console.WriteLine("產品組裝完成");
    }
  }
  // 此時客戶端也要做相應調整
  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()方法用來返回具體產品給客戶端(相當于上面代碼中GetProduct方法)。其中Append方法用來創(chuàng)建產品的組件(相當于上面代碼中BuildPartA和BuildPartB方法),因為string對象中每個組件都是字符,所以也就不需要指揮者的角色的代碼(指的是Construct方法,用來調用創(chuàng)建每個組件的方法來完成整個產品的組裝),因為string字符串對象中每個組件都是一樣的,都是字符,所以Append方法也充當了指揮者Construct方法的作用。
總結
到這里,建造者模式的介紹就結束了,建造者模式(Builder Pattern),將一個復雜對象的構建與它的表示分離,使的同樣的構建過程可以創(chuàng)建不同的表示。建造者模式的本質是使組裝過程(用指揮者類進行封裝,從而達到解耦的目的)和創(chuàng)建具體產品解耦,使我們不用去關心每個組件是如何組裝的。
您可能感興趣的文章
- 01-10深入淺出23種設計模式
 - 01-10解析C#中斷言與異常的應用方式及異常處理的流程控制
 - 01-10深入解析C#編程中struct所定義的結構
 - 01-10深入解析C#中的交錯數組與隱式類型的數組
 - 01-10解析C#中的私有構造函數和靜態(tài)構造函數
 - 01-10解析C#編程的通用結構和程序書寫格式規(guī)范
 - 01-10解析C#中的常量及如何在C#編程中定義常量
 - 01-10解析C#中的分部類和分部方法
 - 01-10深入解析C#中的abstract抽象類
 - 01-10解析C#面向對象編程中方法(method)的使用
 


閱讀排行
本欄相關
- 01-10C#通過反射獲取當前工程中所有窗體并
 - 01-10關于ASP網頁無法打開的解決方案
 - 01-10WinForm限制窗體不能移到屏幕外的方法
 - 01-10WinForm繪制圓角的方法
 - 01-10C#實現txt定位指定行完整實例
 - 01-10WinForm實現仿視頻播放器左下角滾動新
 - 01-10C#停止線程的方法
 - 01-10C#實現清空回收站的方法
 - 01-10C#通過重寫Panel改變邊框顏色與寬度的
 - 01-10C#實現讀取注冊表監(jiān)控當前操作系統(tǒng)已
 
隨機閱讀
- 01-11ajax實現頁面的局部加載
 - 01-10delphi制作wav文件的方法
 - 01-10使用C語言求解撲克牌的順子及n個骰子
 - 01-10C#中split用法實例總結
 - 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
 - 08-05dedecms(織夢)副欄目數量限制代碼修改
 - 08-05DEDE織夢data目錄下的sessions文件夾有什
 - 08-05織夢dedecms什么時候用欄目交叉功能?
 - 04-02jquery與jsp,用jquery
 - 01-10SublimeText編譯C開發(fā)環(huán)境設置
 


