2013年10月21日 星期一

Design Pattern : Builder

Design Pattern : Builder

Builder Strategy 乍看之下很相似,但是前者是Creational pattern,後者是Behavior pattern 另外Buider多了一個Director class來管理Builder class建立物件的流程,隱藏了Constructor的繁雜細節。
Strategy 則比較單純的直接抽換Strategy class改變行為的細節。

底下的範例,是某Lexxx車廠建立車子時, 因為品牌良好,比一般車廠(ICar)多了一些服務:
(a)
專屬業務服務人員
(b)
客戶資料留存
(c)
車子限量序號

以上這些服務需要藉由其他系統產出,而且前端並不須要知道這些建立的細節,這時候就可以使用Builder pattern

1.  原有的ICar:(只有一些基本的property

public interface ICar
{
  DPLib.Color Color { get; set; }
  String Name { get; set; }
  String Equipment { get; set; }
  void Drive();
}



更進階的車廠  ICarMidRange

public interface ICarMidRange:ICar
{
  String SalesName { get; set; } ///
專屬業務服務人員
 
String CustomerName { get; set; } /// 客戶資料
 
String CarUid { get; set; } /// 限量序號
}

定義一台Lexxx的車子: (下面只列出ICarMidRange的實作,ICar的部分請自行補囉)

public class Is300h : ICarMidRange
{
 
public string SalesName { get; set; }
 
public string CustomerName { get; set; }
 
public string CarUid { get; set; }
}


2.  接下來開始進入Builder 我們先建立一個 ICarBuider,這個interface主要是定義及規範建立一台ICarMidRange車子所需要的流程。 

public interface ICarBuider
{
  
///
建立物件
   F Create<F, T>()
where T : F, new();
  
/// 分配 專屬業務服務人員
  
void AssignSales<T>(T car);
  
/// 紀錄 客戶資訊
   void RecordCustomer<T>(T car);
  
/// 註冊 車子限量序號
  
void RegisterCarUid<T>(T car);
}


u  使用泛型的好處,未來若有ICarMidRange的擴充, 例如更高階的ICarExtremeRange,就可以直接拿這個interface來用 J
u  也可以自行練習將泛型直接改為宣告為ICarMidRange類的車子。


3.  實作ICarBuider 的車廠專屬BuilderLexusBuilder

public class LexxxBuilder:ICarBuider
{
   public F Create<F, T>() where T : F, new()
   {

      return new T();       
   }
   void ICarBuider.AssignSales<T>(T car)
   {
      ((
dynamic)car).SalesName = "Luke Skywalker";
   }
  
public void RecordCustomer<T>(T car)
   {
      ((
dynamic)car).CustomerName = "Darth Vader";
   }
  
public void RegisterCarUid<T>(T car)
   {
     
Random rd = new Random(DateTime.Now.Second);
      ((
dynamic)car).CarUid = rd.Next(99999999).ToString().PadLeft(8, '0');
   }
}


4.  最後要實作一個定義Construct流程的Director class
u  Director Constructor 定義了建立車子的流程, 但是使用的方法是用傳入的ICarBuider裡面的方法。 (恩,這邊很像Strategy :P 可以隨時抽換建立的方法
u  請注意需加入一個取得新物件的方法,此例是GetCar()

public class BuildCarDirector :IDisposable
{
  
private ICarMidRange Car;

  
public BuildCarDirector(ICarBuider _carBuider)
   {
    
this.Car =_carBuider.Create<ICarMidRange, Is300h>();
     _carBuider.AssignSales<
ICarMidRange>(this.Car);
     _carBuider.RecordCustomer<
ICarMidRange>(this.Car);
     _carBuider.RegisterCarUid<
ICarMidRange>(this.Car);
   }
  
public ICarMidRange GetCar()
   {
    
return this.Car;
   }

   public void Dispose()
   {
    
if(this.Car!=null)
     {

        this
.Car = null;
     }
   }
}

5.  到這邊已經完整建立了Builder 以下是主程式

///
宣告Builder
ICarBuider carBuilder = new LexxxBuilder();
///宣告Director
using(BuildCarDirector director = new BuildCarDirector( carBuilder ) )
{

   ///
建立車子
  
ICarMidRange myCar = director.GetCar();
  
Console.WriteLine(
        
String.Format("車體序號 {0},客戶 {1},業務 {2}",
         myCar.CarUid, myCar.CustomerName, myCar.SalesName));
}

結果:
使用Builder建立物件 ==============================
車體序號 41478491,客戶 Darth Vader,業務 Luke Skywalker

6.  最後,我們來比較一下BuilderFactory (Abstract Factory) 的使用時機:
u  Factory (Abstract Factory) 用於建立整體。
u  Builder 用於建立繁瑣步驟的物件,尤其是建立過程中需要參考到其他資源者。
u  使用者都不知道建立的細節, 也都可以抽換建立的細節。
u  先考慮Factory (Abstract Factory),再加入Builder (兩者是可結合的)

7.  延伸閱讀:
Design Pattern : Abstract Factory



沒有留言:

張貼留言