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。
而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; }
}
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);
}
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 的車廠專屬Builder:LexusBuilder
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');
}
}
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;
}
}
}
{
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
///宣告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
u 使用者都不知道建立的細節, 也都可以抽換建立的細節。
8.
Reference
Understanding and Implementing Builder Pattern in C#
What is the difference between Builder Design pattern and Factory Design pattern?
Understanding and Implementing Builder Pattern in C#
What is the difference between Builder Design pattern and Factory Design pattern?
沒有留言:
張貼留言