توضیح دوات: برای راهنمایی و اطلاعات بیشتر در مورد نمودار کلاس به این مقاله مراجعه کنید.
الگوی Builder (بیلدر) یک الگوی طراحی creational است که به ما امکان میدهد تا اشیاء پیچیده را مرحله به مرحله ایجاد کنیم. این الگو شیء را از نحوه نمایش آن جدا میکند تا فرآیند ساخت یکسان بتواند نمایشهای مختلفی ایجاد کند.
الگوی Builder زمانی مفید است که مجموعهای از قطعات برای یک شیء پیچیده داریم و نیاز به ساختن همه آنها با هم داریم. با استفاده از این الگو، میتوانیم هر قطعه از شیء پیچیده را مرحله به مرحله مقداردهی اولیه کنیم و سپس آنها را به یک شیء کامل تبدیل کنیم.
شما میتوانید نمونه کد این پست را در Github پیدا کنید.
درک مسئله
تصور کنید یک شیء پیچیده وجود دارد که نیاز به مقداردهی اولیه مرحله به مرحله چندین فیلد دارد که برخی از آنها اختیاری هستند. چنین کدی معمولاً یا در یک سازنده بسیار بزرگ با پارامترهای زیاد قرار میگیرد یا در سازندههای متعدد پراکنده میشود که فقط برخی از فیلدها را مقداردهی اولیه میکنند.
بیایید در مورد ایجاد یک شیء Pizza
فکر کنیم. برای ساختن یک پیتزای ساده، نیاز به یک خمیر ساده، کمی سس گوجه و پنیر رندهشده داریم. اما اگر بخواهیم یک پیتزای بزرگتر و بهتر با پنیر خامهای در لبهها و دیگر افزودنیها (مانند پپرونی، فلفل دلمهای و پنیر موزارلا) داشته باشیم، چه؟
سادهترین راهحل این است که کلاس پایه Pizza
را گسترش دهیم و مجموعهای از زیرکلاسها برای پوشش همه ترکیبهای پارامترها ایجاد کنیم. اما در نهایت، با تعداد زیادی زیرکلاس مواجه خواهید شد. هر پارامتر جدید نیاز به گسترش بیشتر این ساختار دارد.
رویکرد دیگری نیز وجود دارد که شامل ایجاد زیرکلاسهای بیشتر نمیشود. میتوانیم یک سازنده بزرگ در کلاس پایه Pizza
با تمام پارامترهای ممکن که شیء پیتزا را کنترل میکنند، ایجاد کنیم. این رویکرد در واقع نیاز به چندین زیرکلاس را حذف میکند، اما مشکل دیگری ایجاد میکند.
در اکثر موارد، بخش عمدهای از پارامترها استفاده نمیشوند، که باعث زشت شدن فراخوانیهای سازنده میشود. به عنوان مثال، فقط بخشی از پیتزاها از آناناس به عنوان تاپینگ استفاده میکنند، بنابراین پارامترهای مربوط به آن اغلب بیفایده خواهند بود.
الگوی Builder پیشنهاد میکند که کد ساخت شیء را از خود کلاس جدا کرده و آن را به یک شیء جداگانه به نام builder منتقل کنیم.
الگو ساخت شیء را به مجموعهای از مراحل جداگانه (AddPepperoni
، AddBellPeppers
و غیره) سازماندهی میکند. برای ایجاد یک شیء جدید، فقط کافی است مراحل لازم برای تولید یک پیکربندی خاص از شیء را فراخوانی کنیم.
Director
میتوانیم یک گام فراتر برویم و مجموعهای از فراخوانیهای مراحل builder را که برای ساخت یک محصول استفاده میشوند، به یک کلاس جداگانه به نام Director منتقل کنیم. کلاس Director ترتیب اجرای مراحل ساخت را تعیین میکند، در حالی که builder پیادهسازی آن مراحل را فراهم میکند.
داشتن یک Director ضروری نیست. همیشه میتوان مراحل ساخت را به ترتیب خاصی مستقیماً از کد کاربری فراخوانی کرد. با این حال، کلاس Director ممکن است برای داشتن روالهای مختلف ساخت مفید باشد تا بتوان آنها را در سراسر کد دوباره استفاده کرد.
علاوه بر این، کلاس Director جزئیات ساخت محصول را از کد کاربری پنهان میکند. کد فقط نیاز دارد یک builder را به یک Director متصل کند، ساخت را آغاز کند و نتایج را از builder دریافت کند.
ساختار الگوی Builder
نمودار زیر قسمتهای مختلف builder را نشان میدهد.
الگو شامل قسمتهای زیر است:
Director: کلاس Director کلاسی است که تصمیم میگیرد کلاس پیچیده در چه مراحلی باید ساخته شود. در این مثال، نام شیء پیچیده Product است. این کلاس مستقیماً Product را نمیسازد، بلکه از رابط IBuilder برای ایجاد قطعات استفاده میکند.
IBuilder: رابط IBuilder تمام متدهای لازم برای ساخت کلاس Product را تعریف میکند. این متدها برای همه پیادهسازیهای Builder مشترک هستند. Director متد Build را فراخوانی میکند تا نتیجه ساخت را دریافت کند.
ConcreteBuilder: رابط IBuilder را پیادهسازی میکند. یک کلاس Product را گرفته و آن را بهصورت تدریجی میسازد.
Product: شیء Product یک کلاس پیچیده است که ما باید آن را بسازیم.
برای نمایش نحوه عملکرد الگوی Builder، به یکی از بهترین غذاهای نهار یعنی پیتزا نگاهی میاندازیم.
موضوع این است که تنها چیزی که یک پیتزا را تعریف میکند چیزی خوراکی بر روی نوعی خمیر پخته است.
با این حال، انواع مختلفی از پیتزا وجود دارد که نیاز به مراحل مختلفی برای تهیه دارند، اما در نهایت فقط پیتزا هستند. در اکثر موارد، همان مواد میتوانند برای ایجاد انواع مختلف پیتزا استفاده شوند. بیایید ببینیم چگونه میتوانیم از الگوی Builder برای ساخت پیتزاهای خوشمزه استفاده کنیم.
برای شروع، شرکتکننده Director را پیادهسازی خواهیم کرد. کلاس Director خود را AssemblyLine
مینامیم، آن را به یک کلاس تبدیل میکنیم و در آن تعیین میکنیم که مراحل ساخت پیتزا در چه ترتیبی فراخوانی شوند.
///
/// The Director class
///
public class AssemblyLine
{
// Builder uses a complex series of steps
public void Assemble(PizzaBuilder pizzaBuilder)
{
pizzaBuilder.AddDough();
pizzaBuilder.AddSauce();
pizzaBuilder.AddCheeses();
pizzaBuilder.AddMeats();
pizzaBuilder.AddVeggies();
pizzaBuilder.AddExtras();
}
}
ما همچنین باید شرکتکننده Product را تعریف کنیم که توسط شرکتکننده Builder ساخته میشود. برای مثال ما، Product طبیعتاً یک Pizza
است.
///
/// The Product class
///
public class Pizza {
private string pizzaName;
private Dictionary ingredients =
new Dictionary();
public Pizza(string pizzaName) {
this.pizzaName = pizzaName;
}
// Indexer
public string this[string key] {
get {
return ingredients[key];
}
set {
ingredients[key] = value;
}
}
public void Display() {
Console.WriteLine("\n----------------------------");
Console.WriteLine($"Pizza: {pizzaName}");
Console.WriteLine($" Dough: {ingredients["
dough "]}");
Console.WriteLine($" Sauce: {ingredients["
sauce "]}");
Console.WriteLine($" Meats: {ingredients["
meats "]}");
Console.WriteLine($" Cheeses: {ingredients["
cheeses "]}");
Console.WriteLine($" Veggies: {ingredients["
veggies "]}");
Console.WriteLine($" Extras: {ingredients["
extras "]}");
}
}
اکنون که تعریف محصولی که در حال ساخت آن هستیم را میدانیم، شرکتکننده Builder را ایجاد میکنیم. این یک کلاس انتزاعی به نام PizzaBuilder
خواهد بود.
///
/// The Builder Abstract class
///
public abstract class PizzaBuilder
{
protected Pizza pizza;
// Get the pizza instance
public Pizza Pizza
{
get { return pizza; }
}
public abstract void AddDough();
public abstract void AddSauce();
public abstract void AddMeats();
public abstract void AddCheeses();
public abstract void AddVeggies();
public abstract void AddExtras();
}
هر زیرکلاس از PizzaBuilder نیاز دارد تا هر متد انتزاعی را بهدرستی برای ساخت یک پیتزا پیادهسازی کند.
سپس چند کلاس ConcreteBuilder برای ساخت پیتزاهای خاص پیادهسازی میکنیم.
///
/// A ConcreteBuilder class
///
class MeatFeastHot : PizzaBuilder
{
public MeatFeastHot()
{
pizza = new Pizza("Meat Feast Hot");
}
public override void AddDough()
{
pizza["dough"] = "Wheat pizza dough";
}
public override void AddSauce()
{
pizza["sauce"] = "Tomato base";
}
public override void AddMeats()
{
pizza["meats"] = "Pepperoni, Ham, Beef, Chicken";
}
public override void AddCheeses()
{
pizza["cheeses"] = "Signature triple cheese blend, mozzarella";
}
public override void AddVeggies()
{
pizza["veggies"] = "";
}
public override void AddExtras()
{
pizza["extras"] = "jalapenos";
}
}
///
/// A ConcreteBuilder class
///
class HotNSpicyVeg : PizzaBuilder
{
public HotNSpicyVeg()
{
pizza = new Pizza("Hot 'N' Spicy Veg");
}
public override void AddDough()
{
pizza["dough"] = "12-grain pizza dough";
}
public override void AddSauce()
{
pizza["sauce"] = "Tomato base";
}
public override void AddMeats()
{
pizza["meats"] = "";
}
public override void AddCheeses()
{
pizza["cheeses"] = "Signature triple cheese blend, mozzarella";
}
public override void AddVeggies()
{
pizza["veggies"] = "Mushrooms, Peppers, Red Onions";
}
public override void AddExtras()
{
pizza["extras"] = "Jalapenos";
}
}
پس از آماده کردن همه کلاسهای ConcreteBuilder، میتوانیم از آنها در متد Main()
به این صورت استفاده کنیم:
static void Main(string[] args)
{
PizzaBuilder builder;
// Create a pizza assembly line
AssemblyLine shop = new AssemblyLine();
// Construct and display pizzas
builder = new MeatFeastHot();
shop.Assemble(builder);
builder.Pizza.Display();
builder = new HotNSpicyVeg();
shop.Assemble(builder);
builder.Pizza.Display();
// Wait for user
Console.ReadKey();
}
نکته خوب در مورد این الگو این است که اکنون میتوانیم کلاس AssemblyLine
را در هر PizzaBuilder
که میخواهیم استفاده کنیم و کنترل جزئیات بیشتری بر چگونگی ساخت پیتزاها داریم.
مزایا و معایب
مزایا
- ما میتوانیم اشیاء را به صورت مرحله به مرحله بسازیم، مراحل ساخت را به تعویق بیندازیم، یا حتی مراحل را به صورت بازگشتی اجرا کنیم.
- میتوانیم از همان کد ساخت برای ایجاد نمایشهای مختلف از محصولات استفاده کنیم.
- میتوانیم کد ساخت پیچیده را از منطق کسبوکار محصول جدا کنیم و بدین ترتیب اصل تک مسئولیتی را رعایت کنیم.
معایب
- پیچیدگی کلی کد افزایش مییابد زیرا این الگو نیاز به ایجاد چندین کلاس جدید دارد.
ارتباط با دیگر الگوها
الگوی Builder شامل الگوهای مرتبطی است.
- بسیاری از طراحیها به عنوان Factory method آغاز میشوند (پیچیدگی کمتر اما قابل سفارشیسازی بیشتر نسبت به زیرکلاسها) و به سمت Abstract Factories، Prototypes یا Builders (انعطافپذیرتر از Factory method اما پیچیدهتر) تکامل مییابند.
- میتوانیم از Builder هنگام ایجاد درختهای Composite پیچیده استفاده کنیم.
- میتوانیم الگوی Builder را با الگوی Bridge ترکیب کنیم؛ در این حالت، director نقش abstraction را ایفا میکند، در حالی که builders نقش پیادهسازیها را بر عهده دارند.
- Builders اغلب به عنوان Singletons پیادهسازی میشوند.
انواع الگوهای فرعی Builder
Fluent Builder
گسترش الگوی Builder با یک fluent API، خوانایی را افزایش میدهد و همچنین به ما این امکان را میدهد تا عبارات پیکربندی شیء را زنجیرهوار اجرا کنیم. با این کار، دیگر نیازی به مشخص کردن شیء builder با هر عبارت نیست.
public class FluentPizzaBuilder {
private readonly Pizza pizza;
public FluentPizzaBuilder(string pizzaName) {
pizza = new Pizza(pizzaName);
}
public FluentPizzaBuilder WithDough(string dough) {
pizza["dough"] = dough;
return this;
}
public FluentPizzaBuilder WithSauce(string sauce) {
pizza["sauce"] = sauce;
return this;
}
public FluentPizzaBuilder WithMeat(string meat) {
pizza["meats"] = meat;
return this;
}
public FluentPizzaBuilder WithCheese(string cheese) {
pizza["cheeses"] = cheese;
return this;
}
public FluentPizzaBuilder WithVeggie(string veggie) {
pizza["veggies"] = veggie;
return this;
}
public FluentPizzaBuilder WithExtra(string extra) {
pizza["extras"] = extra;
return this;
}
public Pizza Build() {
return pizza;
}
}
اکنون میتوانیم از Fluent Builder برای تولید یک Pizza جدید استفاده کنیم.
var pizzaBuilder = new FluentPizzaBuilder("Supreme");
var pizzaSupreme = pizzaBuilder
.WithDough("12-grain pizza dough")
.WithSauce("Tomato base")
.WithMeat("Pepperoni, Seasoned Minced Beef, Spicy Pork Sausage")
.WithVeggie("Mushroom, Mixed Peppers, Red Onions")
.WithCheese("Mozzarella")
.WithExtra("Jalapenos")
.Build();
pizzaSupreme.Display();
Parent-Child Builder
میتوانیم از رابطه والد-فرزند با الگوی Builder برای ایجاد اشیاء پیچیده استفاده کنیم. ابتدا یک کلاس والد تعریف میکنیم که وظیفه ایجاد شیء پیچیده را بر عهده دارد و سپس یک یا چند کلاس فرزند که بخشهایی از شیء را میسازند.
باید چند Product تعریف کنیم که توسط Child builders ساخته خواهند شد. در این مثال، 3 محصول داریم: یک غذای جانبی (Side dish)، یک ظرف سالاد (Salad dish)، و یک معامله (Deal) که شامل چند Sides و یک Salad است.
public class Side
{
public string Item { get; set; }
public string Dip { get; set; }
public string Size { get; set; }
}
public class Salad
{
public string Base{get;set;}
public string Veggies{get;set;}
public string Meats{get;set;}
public string Cheeses{get;set;}
public string Dressing{get;set;}
}
public class Deal {
public List < Side > Sides {
get;
set;
}
public Salad Salad {
get;
set;
}
public void Display() {
foreach(var side in Sides) {
Console.WriteLine($"Side: {side.Item}");
Console.WriteLine($"Dip: {side.Dip}");
Console.WriteLine($"Size: {side.Size}");
Console.WriteLine();
}
Console.WriteLine("Salad:");
Console.WriteLine($"Base: { Salad.Base}");
Console.WriteLine($"Veggies: {Salad.Veggies}");
Console.WriteLine($"Meats: {Salad.Meats}");
Console.WriteLine($"Cheeses: {Salad.Cheeses}");
Console.WriteLine($"Dressing: {Salad.Dressing}");
}
}
سپس باید برای هر یک از محصولات خود یک بیلدر (Builder) ایجاد کنیم. توجه داشته باشید که DealBuilder به عنوان بیلدر اصلی عمل میکند و SaladBuilder و SideBuilder را فرا میخواند که به عنوان بیلدرهای فرعی عمل میکنند.
public class DealBuilder
{
private readonly Deal deal = new Deal();
public DealBuilder()
{
deal.Sides = new List();
}
public SaladBuilder AddSalad()
{
return new SaladBuilder(this, deal);
}
public SideBuilder AddSide()
{
return new SideBuilder(this, deal);
}
public Deal Build()
{
return deal;
}
}
public class SaladBuilder
{
private readonly DealBuilder _dealBuilder;
private readonly Deal _deal;
private readonly Salad _salad = new Salad();
public SaladBuilder(DealBuilder parentBuilder, Deal deal)
{
_dealBuilder = parentBuilder;
_deal = deal;
}
public SaladBuilder WithBase(string saladBase)
{
_salad.Base = saladBase;
return this;
}
public SaladBuilder WithVeggies(string veggies)
{
_salad.Veggies = veggies;
return this;
}
public SaladBuilder WithMeats(string meats)
{
_salad.Meats = meats;
return this;
}
public SaladBuilder WithCheeses(string cheeses)
{
_salad.Cheeses = cheeses;
return this;
}
public SaladBuilder WithDressing(string dressing)
{
_salad.Dressing = dressing;
return this;
}
public DealBuilder BuildSalad()
{
_deal.Salad = _salad;
return _dealBuilder;
}
}
public class SideBuilder
{
private DealBuilder dealBuilder;
private Deal deal;
private Side side = new Side();
public SideBuilder(DealBuilder dealBuilder, Deal deal)
{
this.dealBuilder = dealBuilder;
this.deal = deal;
}
public SideBuilder WithItem(string item)
{
side.Item = item;
return this;
}
public SideBuilder WithDip(string dip)
{
side.Dip = dip;
return this;
}
public SideBuilder WithSize(string size)
{
side.Size = size;
return this;
}
public DealBuilder BuildSide()
{
deal.Sides.Add(side);
return dealBuilder;
}
}
حالا میتوانیم از Builder برای ایجاد شیء Deal استفاده کنیم:
var deal = new DealBuilder()
.AddSide()
.WithItem("Spicy Loaded Pepperoni Wedges")
.WithDip("Mayo")
.WithSize("Large")
.BuildSide()
.AddSide()
.WithItem("Spicy Cheesy Pepperoni Garlic Bread")
.WithDip("BBQ Sauce")
.WithSize("Large")
.BuildSide()
.AddSalad()
.WithBase("Lettuce")
.WithVeggies("Cherry Tomatoes, Red Cabbage, Carrot")
.WithDressing("Garlic & Herbs")
.BuildSalad()
.Build();
Progressive Builder
یک Progressive Builder نسخهای از الگوی Builder است که به ترتیب از چندین builder استفاده میکند تا یک توالی ثابت از فراخوانیهای زنجیرهای متدها را تعریف کند. مزیت این پیادهسازی این است که تضمین میکند شیء به ترتیب صحیح ساخته میشود.
در مثال زیر، یک Pizza Deal ایجاد خواهیم کرد که شامل چند غذای جانبی، یک سالاد، یک پیتزا، نوشیدنی و دسر خواهد بود.
همانطور که همیشه انجام میدهیم، ابتدا کلاسهای محصولات خود را تعریف میکنیم.
public class Side {
public string Item {
get;
set;
}
public string Dip {
get;
set;
}
public string Size {
get;
set;
}
public void Display() {
Console.WriteLine("\n--------- Side Dish ---------");
Console.WriteLine($"Item: {Item}");
Console.WriteLine($"Dip: {Dip}");
Console.WriteLine($"Size: {Size}");
}
}
public class Salad {
public string Base {
get;
set;
}
public string Veggies {
get;
set;
}
public string Meats {
get;
set;
}
public string Cheeses {
get;
set;
}
public string Dressing {
get;
set;
}
public void Display() {
Console.WriteLine("\n--------- Salad Dish ---------");
Console.WriteLine($"Base: {Base}");
Console.WriteLine($"Veggies: {Veggies}");
Console.WriteLine($"Meats: {Meats}");
Console.WriteLine($"Cheeses: {Cheeses}");
Console.WriteLine($"Dressing: {Dressing}");
}
}
public class Drink {
public string Item {
get;
set;
}
public string Size {
get;
set;
}
public void Display() {
Console.WriteLine("\n--------- Drink Dish ---------");
Console.WriteLine($"Item: {Item}");
Console.WriteLine($"Size: {Size}");
}
}
public class Dessert {
public string Item {
get;
set;
}
public string Size {
get;
set;
}
public void Display() {
Console.WriteLine("\n--------- Dessert Dish ---------");
Console.WriteLine($"Item: {Item}");
Console.WriteLine($"Size: {Size}");
}
}
و کلاس Deal:
public class MenuDeal {
public List < Side > Sides {
get;
set;
}
public Salad Salad {
get;
set;
}
public Pizza Pizza {
get;
set;
}
public Drink Drink {
get;
set;
}
public Dessert Dessert {
get;
set;
}
public MenuDeal() {
Sides = new List < Side > ();
}
public void Display() {
foreach(var side in Sides)
side.Display();
Salad.Display();
Pizza.Display();
Drink.Display();
Dessert.Display();
}
}
در قسمت بعد، کلاسهای Builder را تعریف میکنیم. توجه کنید که هر Builder میتواند Builder بعدی زنجیزه را فراخوانی کند.
public class DessertBuilder {
private MenuDealBuilder menuDealBuilder;
private MenuDeal menuDeal;
private Dessert dessert;
public DessertBuilder(MenuDealBuilder menuDealBuilder, MenuDeal menuDeal) {
this.menuDealBuilder = menuDealBuilder;
this.menuDeal = menuDeal;
dessert = new Dessert();
}
public DessertBuilder WithItem(string item) {
dessert.Item = item;
return this;
}
public DessertBuilder WithSize(string size) {
dessert.Size = size;
return this;
}
public MenuDeal Build() {
menuDeal.Dessert = dessert;
return menuDeal;
}
}
public class SideDishBuilder {
private MenuDealBuilder menuDealBuilder;
private MenuDeal menuDeal;
private Side sideDish;
public SideDishBuilder(MenuDealBuilder menuDealBuilder, MenuDeal menuDeal) {
this.menuDealBuilder = menuDealBuilder;
this.menuDeal = menuDeal;
sideDish = new Side();
}
public SideDishBuilder WithItem(string item) {
sideDish.Item = item;
return this;
}
public SideDishBuilder WithDip(string dip) {
sideDish.Dip = dip;
return this;
}
public SideDishBuilder WithSize(string size) {
sideDish.Size = size;
return this;
}
public SideDishBuilder AddSideDish() {
menuDeal.Sides.Add(sideDish);
return new SideDishBuilder(menuDealBuilder, menuDeal);
}
public SaladBuilder AddSaladDish() {
menuDeal.Sides.Add(sideDish);
return new SaladBuilder(menuDealBuilder, menuDeal);
}
}
public class SaladBuilder {
private MenuDealBuilder menuDealBuilder;
private MenuDeal menuDeal;
private Salad salad;
public SaladBuilder(MenuDealBuilder menuDealBuilder, MenuDeal menuDeal) {
this.menuDealBuilder = menuDealBuilder;
this.menuDeal = menuDeal;
salad = new Salad();
}
public SaladBuilder WithBase(string saladBase) {
salad.Base = saladBase;
return this;
}
public SaladBuilder WithVeggies(string veggies) {
salad.Veggies = veggies;
return this;
}
public SaladBuilder WithMeats(string meats) {
salad.Meats = meats;
return this;
}
public SaladBuilder WithCheeses(string cheeses) {
salad.Cheeses = cheeses;
return this;
}
public SaladBuilder WithDressing(string dressing) {
salad.Dressing = dressing;
return this;
}
public PizzaBuilder AddPizzaDish() {
menuDeal.Salad = salad;
return new PizzaBuilder(menuDealBuilder, menuDeal);
}
}
public class PizzaBuilder {
private MenuDealBuilder menuDealBuilder;
private MenuDeal menuDeal;
private Pizza pizza;
public PizzaBuilder(MenuDealBuilder menuDealBuilder, MenuDeal menuDeal) {
this.menuDealBuilder = menuDealBuilder;
this.menuDeal = menuDeal;
pizza = new Pizza();
}
public PizzaBuilder WithDough(string dough) {
pizza.Dough = dough;
return this;
}
public PizzaBuilder WithSauce(string sauce) {
pizza.Sauce = sauce;
return this;
}
public PizzaBuilder WithCheeses(string cheeses) {
pizza.Cheeses = cheeses;
return this;
}
public PizzaBuilder WithMeats(string meats) {
pizza.Meats = meats;
return this;
}
public PizzaBuilder WithVeggies(string veggies)
{
pizza.Veggies = veggies;
return this;
}
public PizzaBuilder WithExtras(string extras)
{
pizza.Extras = extras;
return this;
}
public DrinkBuilder AddDrink()
{
menuDeal.Pizza = pizza;
return new DrinkBuilder(menuDealBuilder, menuDeal);
}
}
public class DrinkBuilder {
private MenuDealBuilder dealBuilder;
private MenuDeal menuDeal;
private Drink drink;
public DrinkBuilder(MenuDealBuilder dealBuilder, MenuDeal menuDeal) {
this.dealBuilder = dealBuilder;
this.menuDeal = menuDeal;
drink = new Drink();
}
public DrinkBuilder WithItem(string item) {
drink.Item = item;
return this;
}
public DrinkBuilder WithSize(string size) {
drink.Size = size;
return this;
}
public DessertBuilder AddDesert() {
menuDeal.Drink = drink;
return new DessertBuilder(dealBuilder, menuDeal);
}
}
public class DessertBuilder {
private MenuDealBuilder menuDealBuilder;
private MenuDeal menuDeal;
private Dessert dessert;
public DessertBuilder(MenuDealBuilder menuDealBuilder, MenuDeal menuDeal) {
this.menuDealBuilder = menuDealBuilder;
this.menuDeal = menuDeal;
dessert = new Dessert();
}
public DessertBuilder WithItem(string item) {
dessert.Item = item;
return this;
}
public DessertBuilder WithSize(string size) {
dessert.Size = size;
return this;
}
public MenuDeal Build() {
menuDeal.Dessert = dessert;
return menuDeal;
}
}
آخرین مرحله فراخوانی زنجیره Builder ها به منظور ایجاد شیء deal است.
var menuBuilder = new MenuDealBuilder();
var menu = menuBuilder
.AddSideDish()
.WithItem("Breaded All-White Chicken Breast, Baked in Stone Oven")
.WithDip("Frank's Spicy Buffalo")
.WithSize("Large")
.AddSideDish()
.WithItem("Gluten Free Corn Tortilla Chips")
.WithDip("Guacamole")
.WithSize("Large")
.AddSaladDish()
.WithBase("Fresh Mixed Lettuce")
.WithVeggies("Chopped Onions, Green Bell Peppers, Black Olives, Mushrooms")
.WithMeats("Fajita Chicken, Bacon Bits")
.WithCheeses("Mozzarella")
.WithDressing("Zesty Italian")
.AddPizzaDish()
.WithDough("Sourdough with Cream Cheese Crust")
.WithSauce("Tomato Sauce")
.WithMeats("Beef, Sausage, Pepperoni")
.WithVeggies("Mushrooms, Green Bell Peppers, Onions")
.WithCheeses("Mozzarella")
.AddDrink()
.WithItem("Soda")
.WithSize("Large")
.AddDesert()
.WithItem("Cookie Dough Ice Cream")
.WithSize("Large")
.Build();
menu.Display();
هر Builder بخش خود را از شیء بزرگ Deal میسازد و سپس Builder بعدی در زنجیره را فراخوانی میکند. به این صورت اطمینان حاصل میشود که بخشها با ترتیب درست ایجاد میشوند.
نتیجهگیری نهایی
در این مقاله، درباره الگوی Builder، زمان استفاده از آن و مزایا و معایب استفاده از این الگو صحبت کردیم. سپس نحوه ارتباط الگوی Builder با سایر الگوهای طراحی کلاسیک را بررسی کردیم. در نهایت، برخی از نسخههای الگوی Builder را مشاهده کردیم که یا خوانایی الگو را بهبود میبخشند (FluentBuilder) یا یک مسئله خاص را حل میکنند.
توجه به این نکته ضروری است که الگوی Builder، همراه با سایر الگوهای طراحی ارائهشده توسط Gang of Four، راهحل نهایی و کامل برای طراحی یک برنامه نیست. باز هم این وظیفه مهندسان است که بررسی کنند چه زمانی باید از یک الگوی خاص استفاده کنند. در نهایت این الگوها زمانی مفید هستند که بهعنوان یک ابزار دقیق مورد استفاده قرار گیرند، نه یک چکش سنگین.