الگوی طراحی Builder یکی از الگوهای طراحی Creational می باشد که به شما اجازه میدهد شیءهای پیچیده را گام به گام ساخته و تشکیل دهید. این الگو به شما امکان میدهد از همان کد ساخت یک شیء ، انواع و نمایشهای مختلفی از آن شیء را ایجاد کنید.
مسئله : تصور کنید یک شیء پیچیده وجود دارد که نیاز به مقداردهی مرحله به مرحله و دقیق بسیاری از فیلدها و شیء های تودرتو دارد. چنین کدی معمولاً درون یک سازنده هولناک با تعداد زیادی پارامتر قرار دارد. یا حتی بدتر در سراسر کد مشتری پراکنده شده است.
به عنوان مثال : فرض کنید که میخواهید یک شیء خانه (House) ایجاد کنید. برای ساخت یک خانه ساده، شما نیاز به ساخت چهار دیوار و یک زمین، نصب یک در، نصب یک جفت پنجره و ساخت یک سقف دارید. اما اگر میخواهید یک خانه بزرگتر، روشنتر با یک حیاط پشتی و سایر امکانات (مانند یک سیستم گرمایشی، لولهکشی و سیمکشی برق) داشته باشید؟ سادهترین راهحل این است که کلاس پایه House را گسترش دهید و یک مجموعه زیرکلاس ایجاد کنید تا همهی ترکیبهای پارامترها را پوشش دهند. اما در نهایت، شما با تعداد قابل توجهی از زیرکلاسها مواجه خواهید شد. هر پارامتر جدید، مانند سبک تراس، نیاز به افزایش این سلسله مراتب را بیشتر میکند. یک رویکرد دیگر وجود دارد که نیازی به ایجاد زیرکلاسها ندارد. شما میتوانید یک سازنده عظیم را در کلاس پایه House ایجاد کنید که تمام پارامترهای ممکنی را که کنترل شیء خانه را دارند، شامل شود. اگرچه این رویکرد واقعاً نیاز به زیرکلاسها را از بین میبرد، اما مشکل جدیدی ایجاد میکند.
در اکثر موارد، بیشتر پارامترها استفاده نخواهند شد که باعث میشود فراخوانیهای سازنده بسیار زشت باشند. به عنوان مثال ، تنها یک بخش کوچک از خانهها استخر دارند، بنابراین پارامترهای مربوط به استخر در نهایت ده مورد از ده مورد بیاستفاده خواهند بود.
راه حل : الگوی Builder پیشنهاد میدهد که کد ساخت شیء را از کلاس خود آن استخراج کرده و به اشیاء جداگانه به نام سازندگان (Builders) انتقال دهید.
الگو اقدامات ساخت شیء را به یک مجموعه مراحل (مانند ساخت دیوارها، ساخت در و غیره) سازماندهی میکند. برای ایجاد یک شیء ، شما یک سری از این مراحل را بر روی یک شیء سازنده اجرا میکنید. بخش مهم این است که نیازی به فراخوانی همهی مراحل نیست. میتوانید فقط آن مراحلی را که برای تولید یک پیکربندی خاص از یک شیء لازم است، فراخوانی کنید. بعضی از مراحل ساخت ممکن است در صورت نیاز به ایجاد نمایشهای مختلف محصول، پیادهسازی متفاوتی داشته باشند. به عنوان مثال ، دیوارهای یک کلبه ممکن است از چوب ساخته شوند، اما دیوارهای قلعه باید از سنگ ساخته شوند. در این صورت، میتوانید چندین کلاس سازنده مختلف ایجاد کنید که همان مجموعه مراحل ساخت را به یک نحو متفاوت پیادهسازی کنند. سپس میتوانید این سازندگان را در فرآیند ساخت (یعنی یک مجموعه مرتب از فراخوانیهای مراحل ساخت) برای تولید انواع مختلفی از اشیاء استفاده کنید. به عنوان مثال ، تصور کنید یک سازنده وجود دارد که همه چیز را از چوب و شیشه میسازد، یک دیگر که همه چیز را از سنگ و آهن میسازد و یک سوم که از طلا و الماس استفاده میکند. با فراخوانی همان مجموعه مراحل، شما یک خانه معمولی از سازنده اول، یک قلعه کوچک از دوم و یک کاخ از سوم دریافت میکنید. با این حال، این فقط در صورتی کار خواهد کرد که کد مشتری که مراحل ساخت را فراخوانی میکند، قادر به ارتباط با سازندگان با استفاده از یک رابط مشترک باشد. میتوانید بیشتر بروید و یک سری از فراخوانیها به مراحل سازنده که برای ساخت یک محصول استفاده میکنید، را به یک کلاس جداگانه به نام مدیر استخراج کنید. کلاس مدیر ترتیبی را که مراحل ساخت را اجرا کنید تعریف میکند، در حالی که سازنده پیادهسازی برای این مراحل را فراهم میکند. داشتن یک کلاس مدیر (Director) در برنامه شما ضروری نیست. همیشه میتوانید مراحل ساخت را به ترتیب مشخص مستقیماً از کد مشتری فراخوانی کنید. با این حال، کلاس مدیر ممکن است یک مکان مناسب برای قرار دادن روالهای مختلف ساخت باشد تا بتوانید از آنها در سراسر برنامه خود استفاده مجدد کنید. علاوه بر این، کلاس مدیر جزئیات ساخت محصول را کاملاً از کد مشتری پنهان میکند. مشتری تنها نیاز دارد یک سازنده را با یک مدیر مرتبط کند، ساخت را با مدیر شروع کند، و نتیجه را از سازنده دریافت کند.
کلاس دیاگرام :
گام اول ، Builder : مراحل ساخت محصول را که برای همه انواع سازندگان مشترک است ، اعلام میکند.
گام دوم ، Concrete Builder : پیاده سازی های مختلف و همچنین پیاده سازی Builder توسط Concrete Builder انجام می شود ، این Concrete Builder ها ممکن است محصولاتی را تولید کنند که با رابط مشترک سازگار نیستند.
گام سوم ، Director : ترتیبی را که باید مراحل ساخت را فراخوانی کرد ، تعریف میکند تا بتوانید پیکربندی های خاصی از محصولات را ایجاد و بازیافت کنید.
گام چهارم ، Product : شیء هایی نهایی هستند که توسط سازندگان مختلف (Builder) ایجاد شده اند و متعلق به هیچ سلسله مراتب یا واسط نیستند.
گام پنجم ، Client : مشتری باید یکی از اشیاء سازنده (Builder) را با مدیر (Director) مرتبط کند. معمولا این کار تنها یک بار ، از طریق پارامترهای سازنده مدیر (Director) انجام می شود. سپس Director از آن شیء Builder برای همه ساخت های بعدی استفاده می کند. البته رویکرد جایگزینی هم وجود دارد که در آن Client شیء Builder را به متد Production در Director پاس می دهد. در این صورت شما می توانید هر بار که با Director یک Product ایجاد می کنید ، از یک Builder متفاوت استفاده کنید.
مثالی ساده از پیاده سازی الگوی Builder به زبان #C ، در زمینه ساخت یک سیستم سفارش آنلاین برای یک فروشگاه آنلاین :
کلاس Order ، این کلاس نمایانگر سفارشات است و ویژگی هایی مانند : محصولات ، تعداد ، آدرس تحویل و وضعیت پرداخت را دارد :
public class Order { public List<string> Products { get; set; } = new List<string>(); public List<int> Quantities { get; set; } = new List<int> { 0 }; public string DeliveryAddress { get; set; } public bool IsPaid { get; set; } public void Display() { Console.WriteLine("Order Details:"); for (int i = 0; i < Products.Count; i++) { Console.WriteLine($"Product: {Products[i]}, Quantity: {Quantities[i]}"); } Console.WriteLine("Delivery Address: " + DeliveryAddress); Console.WriteLine("Payment Status: " + (IsPaid ? "Paid" : "Not Paid")); } }
واسط IOrderBuilder تعیین می کند که چه مراحلی برای ساخت یک سفارش لازم است :
public interface IOrderBuilder { void AddProduct(string product, int quantity); void SetDeliveryAddress(string address); void SetPaymentStatus(bool isPaid); Order GetOrder(); }
کلاس OnlineOrderBuilder مسئول ساخت سفارشات آنلاین است و مراحل مختلف ساخت را انجام میدهد :
public class OnlineOrderBuilder : IOrderBuilder { private Order _order = new Order (); public void AddProduct(string product, int quantity) { _order.Products.Add(product); _order.Quantities.Add(quantity); } public Order GetOrder() { return _order; } public void SetDeliveryAddress(string address) { _order.DeliveryAddress = address; } public void SetPaymentStatus(bool isPaid) { _order.IsPaid = isPaid; } }
کلاس OrderProcessor به عنوان Director عمل می کند و مراحل ساخت سفارشات را کنترل می کند :
public class OrderProcessor { private IOrderBuilder _builder; public OrderProcessor(IOrderBuilder builder) { _builder = builder; } public void ConstructOrder() { _builder.AddProduct("Laptop", 1); _builder.AddProduct("Phone", 2); _builder.SetDeliveryAddress("123 Main St"); _builder.SetPaymentStatus(true); } public Order GetOrder() { return _builder.GetOrder(); } }
حالا میتوانیم از کلاس OrderProcessor به عنوان Director و کلاس OnlineOrderBuilder به عنوان ConcreteBuilder برای ساخت سفارشات آنلاین استفاده کنیم.
public class Program { static void Main(string[] args) { OnlineOrderBuilder builder = new OnlineOrderBuilder(); OrderProcessor processor = new OrderProcessor(builder); processor.ConstructOrder(); Order onlineOrder = processor.GetOrder(); onlineOrder.Display(); } }
در این مثال ، با استفاده از OrderProcessor و OnlineOrderBuilder یک سفارش آنلاین ساخته شده است. سپس با استفاده از متد ;()Display ، جزئیات سفارش نمایش داده شده است.
الگوی طراحی Builder مزایا و معایبی دارد که بهتر است در نظر گرفته شوند :
مزایا :
معایب :
در کل ، الگوی طراحی Builder مناسب برای ساخت شیءهای پیچیده با ترکیب مراحل مختلف ساخت است، اما باید توجه داشت که استفاده از آن باید با توجه به نیازها و شرایط ویژه هر پروژه انجام شود.
منبع :