در سال 1994 گروه 4 نفره Gang of Four که به اختصار با نام GOF شناخته میشوند 23 الگوی طراحی را که توسط کریستوف الکساندر (Christopher Alexander) توسعه داده شده بودند، طبقه بندی و ارائه کردند. یکی از الگوهای طراحی که توسط آنها به منظور ساخت اشیا در فرآیند برنامه نویسی نرم افزارها ارائه شد، الگوی طراحی Builder یا سازنده نام دارد.
این الگوی طراحی یک الگوی طراحی سازنده یا Creational است که با هدف تسهیل ساخت اشیای پیچیده در طراحی نرم افزار به کار برده میشود. این الگو به شما کمک میکند که پیچیدهترین اشیا را به صورت گام به گام و به وسیله اشیای سادهتر ایجاد کنید تا بتوانید انواع مختلفی از یک شی را با استفاده از همان ساختار پایه تولید کنید.
چرا باید از الگوی طراحی Builder استفاده کنیم
گاهی در فرآیند طراحی یک نرم افزار نیاز به ساخت و تعریف اشیایی پیچیده با پارامترهای زیاد خواهید داشت. عملیات مقداردهی اولیه پارامترهای همچین اشیایی در سازنده ی آنها (Constructor) تصور کنید. برای این کار باید تعداد زیادی پارامتر را به سازنده آن اشیا ارسال شود، که این کار باعث افزایش پیچیدگی و کاهش خوانایی کدها در نرم افزار خواهد شد.
به عنوان مثال، فرض کنید که میخواهید یک خانه طراحی و ایجاد کنید. برای ساخت یک خانه ساده، شما تنها نیاز به ساخت چهار دیوار و یک کف، نصب درب، جایگذاری پنجره و در نهایت ساخت یک سقف خواهید داشت. برای این کار شما باید یک کلاس (Class) خانه ایجاد کنید. اما اگر تصمیم بگیرید که یک خانه بزرگتر و روشنتر با یک حیاط خلوت و سایر امکانات رفاهی از جمله سیستم گرمایشی، لوله کشی، سیم کشی برق و... داشته باشید، آیا به سادگی میتوانید همه این امکانات را در کلاس خانه اضافه کنید؟
قطعا خیر! زیرا در آن صورت باید تعداد بسیار زیادی پارامتر را برای مقدار دهی به سازنده (Constructor) کلاس خانه ارجاع دهید. این عمل باعث افزایش پیچیدگی و کاهش خوانایی کدهای نرم افزار خواهد شد. همچنین امکان دارد در اغلب موارد، بسیاری از پارامترها استفاده نشده باقی بمانند، که باعث کثیف شدن کدنویسی میشوند. به عنوان مثال، اگر فقط یک خانه دارای استخر شنا باشد، پارامترهای مربوط به استخر شنا برای ساخت خانههای بدون استخر خالی (null) خواهند ماند.
همچنین میتوانید انواع امکانات و ساختارهای مختلف را برای خانه ای که در نظر گرفته اید، پیش بینی و شناسایی کنید. پس از شناسایی این موارد، هر کدام از آنها را یک کلاس فرزند (subclass) از کلاس خانه (superclass) در نظر بگیرید. سپس به کمک یک Interface یا کلاس Abstract، کلاس والد (خانه) را با کلاسهای مورد نظر گسترش (extend) میدهیم. با این کار اگر در آینده امکاناتی جدید برای افزودن به خانه در نظر گرفته شد، راحتتر میتوانید آن را پیاده سازی کنید. زیرا در این روش شما یک کلاس خانه دارید که در هر زمان میتوانید آن را به وسیله مجموعه ای از کلاسهای فرزند گسترش دهید.
شاید از نظر شما این سادهترین و بهترین راه است که کلاس پایه (خانه) را گسترش داده و مجموعه ای از کلاسهای فرزند را برای پوشش ترکیبها و امکانات مختلف پارامترها ایجاد کنید. اما این روش در نهایت شما را با تعداد قابل توجهی از کلاسهای فرزند رو به رو خواهد کرد. نکته قابل توجه در این روش این است که با اضافه شدن امکانات و پارامترهای جدید، تعداد کلاسهای فرزند بیشتر و بیشتر خواهد شد. به همین منظور رویکردی بهینه برای انجام اینگونه کارها به وجود آمد که همان الگوی طراحی Builder است.
راه حل ارائه شده توسط الگوی طراحی Builder
الگوی سازنده روشی را برای ساخت اشیا پیچیده پیشنهاد میدهد که روند ساخت آنها را بسیار سادهتر میکند. در این روش به جای اینکه پارامترهای زیادی را به یک Constructor ارسال شود یا از تعداد زیادی Constructor استفاده شود، کلاسی را ایجاد میکنند که وظیفه ساخت اشیا را بر عهده بگیرد. برای این کار باید کدهایی که وظیفه ساخت اشیا را بر عهده دارند از کلاس مربوط به اشیا بیرون کشیده شوند. سپس باید این کدها در کلاسی جدید قرار گیرند که به آن کلاس Builder میگویند. از این پس، برای ساخت اشیا از کلاس Builder استفاده میشود.
کلاس builder ساخت بخشهای مختلف شی را به مجموعه ای از مراحل تقسیم بندی میکند. مثلا ساختن دیوارها، درها و... . مزیتی که این روش نسبت به روشهای قبلی دارد این است که دیگر نیازی به انجام تمام مراحل برای ساخت یک شی نخواهد بود. بر این اساس برای ساخت یک شی با ساختاری خاص، تنها مراحل مورد نیاز انجام میشوند. امکان دارد برخی از مراحل ساخت یک شی نسبت به اشیای دیگر متفاوت باشد. به عنوان مثال، دیوارهای یک کلبه ممکن است از چوب ساخته شوند، اما دیوارهای قلعه باید از جنس سنگ باشند.
همانطور که در مثال بالا مشاهده میکنید، سازندگان مختلف میتوانند همان مراحل ساخت را به شیوههای متفاوت انجام دهند. در این موارد، میتوانید چند کلاس Builder ایجاد کنید که همان مراحل ساخت را با شیوه هایی متفاوت اجرا کنند. سپس میتوانید از این کلاسها در روند ساخت اشیا مختلف استفاده کنید. به عنوان مثال تصور کنید که سازنده اول یک خانه را با استفاده از چوب و شیشه بسازد. سازنده دوم برای این کار از سنگ و آهن برای ساخت و ساز خانه استفاده میکند. در نهایت سومین سازنده فقط طلا و الماس را به عنوان مصالح ساخت به کار میگیرد.
بنابراین با فراخوانی هر یک از این سازندگان به منظور ساخت یک خانه، در حالی که همه آنها مراحل مشابه ای برای ساخت آن خانه را طی کرده اند ولی خانه هایی متفاوت به شما تحویل خواهند داد. اولین سازنده خانه ای مانند کلبه و از جنس چوب برای شما آماده کرده است. دومین سازنده با استفاده از سنگ و آهن یک قلعه مستحکم را ساخته است. در نهایت سومین سازنده خانه ای گران قیمت از جنس طلا و الماس در اختیار شما قرار خواهد داد. بنابراین با استفاده از الگوی طراحی Builder میتوانید انواع مختلفی از یک شی پیچیده را با مراحلی مشخص ایجاد کنید. اگرچه این کار زمانی امکان پذیر است که کدی که مراحل ساخت اشیا را فراخوانی میکند قادر به برقراری ارتباط با کلاسهای سازنده از طریق یک Interface مشترک باشند.
همچنین میتوانید یک کلاس به نام Director ایجاد کنید که مراحل ساخت اشیا را به کلاسهای سازنده بدهد. به عبارتی دیگر کلاس Director وظیفه دارد مراحل ساخت اشیا را تعیین کند تا کلاسهای سازنده این مراحل را اجرا کنند. ساختن یک کلاس Director در نرم افزار ضروری نخواهد بود. کلاس Director مانند یک پیمانکار ساختمانی است که وظیفه دارد وظایف و مراحل ساخت یک خانه را برای سازندگان آن مشخص کند. بنابراین برای ساخت اشیا، دیگر نیازی به کار با کلاسهای سازنده نخواهد بود و فقط کافی است کلاس Director به کلاسهای سازنده متصل شود. سپس برای ساخت کلاس ها، کلاس Director را اجرا شود تا در نهایت نتایج نهایی از همین کلاس دریافت شوند.
کاربردهای الگوی طراحی Builder
به کارگیری الگوی طراحی سازنده در طراحی نرم افزارها، با افزایش انعطاف پذیری و خوانایی کدها همراه خواهد بود. البته از طرفی دیگر پیاده سازی این الگو نیازمند ساخت چندین کلاس و تابع اضافه است. الگوی طراحی سازنده در موارد زیر کاربردی خواهد بود :
امکان ساخت نمونههای مختلفی از یک شی را فراهم میسازد (برای مثال خانههای چوبی، سنگی و...).
باعث جلوگیری از ساخت تعداد زیادی Constructor برای ساخت اشیا میشود.
ساخت اشیای پیچیده و مرکب را سادهتر میکند.
روش پیاده سازی الگوی طراحی Builder
برای پیاده سازی الگوی طراحی سازنده نیازمند طی کردن چندین گام خواهیم بود. پس از آشنایی با مراحل پیاده سازی این الگوی طراحی، به بررسی مثالی از الگوی طراحی سازنده با استفاده زبان برنامه نویسی PHP خواهیم پرداخت. در این مثال مراحل ساخت یک خانه با استفاده از الگوی طراحی سازنده را بررسی خواهیم کرد. مراحل پیاده سازی این الگو عبارتند از :
ابتدا باید از این موضوع اطمینان حاصل شود که به طور واضح و دقیق تمام اقدامات و مراحل لازم برای ساخت تمامی نمونه های شی مورد نظر (محصول) تعریف شده باشند. در غیر اینصورت پیاده سازی این الگوی طراحی امکان پذیر نخواهد بود.
سپس این مراحل باید در یک Interface با نام Builder تعریف شوند.
در سومین گام باید یک کلاس Builder برای هر یک از نمونههای شی مورد نظر ایجاد شود. این کلاسها باید Interface ساخته شده در گام قبلی را Implement کنند. به عبارتی دیگر همه آنها باید از مراحل ساخت و ساز تعریف شده پیروی کنند.
همچنین میتوان از کلاس دیگری به نام Director برای مدیریت و پیاده سازی مراحل قبل استفاده کرد. این کلاس می تواند از راههای مختلفی به ساخت اشیا با استفاده از همان Builderها بپردازد.
بنابراین ابتدا باید کلاس های Director و Builder ایجاد شوند. سپس باید قبل از اینکه عملیات ساخت شی آغاز شود، کلاس Builder به کلاس Director مرتبط شود. این ارتباط یک بار به وسیله ارسال پارامترهای کلاس Builder در Constructor کلاس Director ایجاد میشود. با این کار کلاس Director در ساخت و سازهای بعدی از Builder استفاده میکند. البته برای این منظور یک راه حل جایگزین نیز وجود دارد. در راه حل دوم میتوان Builderها را به صورت مستقیم در تابعی (Method) که وظیفه ساخت در کلاس Director را دارد، وارد کنید.
در نهایت اگر تمام اشیا از همان Interface پیروی کنند میتوان نتایج ساخت و ساز را به صورت مستقیم از کلاس Director دریافت کرد. در غیر اینصورت باید نتایج از همان کلاس Builder دریافت شوند.
<?php
/**
*/
interface Builder
{
public function setBasement(): void;
public function setStructure(): void;
public function setRoof(): void;
}
/**
*/
class House implements Builder
{
private $House;
/**
*/
public function __construct()
{
$this->reset();
}
public function reset(): void
{
$this->house = new House1;
}
/**
*/
public function setBasement(): void
{
$this->house->parts[] = "basement";
}
public function setStructure(): void
{
$this->house->parts[] = "structure";
}
public function setRoof(): void
{
$this->house->parts[] = "roof";
}
/**
*
*/
public function getHouse(): House1
{
$result = $this->House;
$this->reset();
return $result;
}
}
/**
*/
class House1
{
public $parts = [];
public function listParts(): void
{
echo "House parts: " . implode(', ', $this->parts) . "\n\n";
}
}
/**
*/
class Director
{
/**
*/
private $builder;
/**
*/
public function setBuilder(Builder $builder): void
{
$this->builder = $builder;
}
/**
* The Director can construct several house variations using the same
* building steps.
*/
public function buildMinimalViableHouse(): void
{
$this->builder->produceBasement();
}
public function buildFullFeaturedHouse(): void
{
$this->builder->produceBasement();
$this->builder->produceStructure();
$this->builder->produceRoof();
}
}
/**
*/
function clientCode(Director $director)
{
$builder = new House;
$director->setBuilder($builder);
echo "Standard basic House:\n";
$director->buildMinimalViableHouse();
$builder->getHouse()->listParts();
echo "Standard full featured House:\n";
$director->buildFullFeaturedHouse();
$builder->getHouse()->listParts();
// Remember, the Builder pattern can be used without a Director class.
echo "Custom House:\n";
$builder->produceBasement();
$builder->produceStructure();
$builder->getHouse()->listParts();
}
$director = new Director;
clientCode($director);
اگر میخواهید بیشتر راجب الگوهای طراحی بدانید, مقالات زیر را مطالعه کنید
الگوی طراحی سازنده راه حل بسیار مطمئنی برای حل مشکلات مربوط به ساخت اشیا پیچیده ارائه میدهد. با استفاده از این تکنیک به راحتی میتوان نمونههای مختلفی از یک شی با پارامترها و خصوصیات متفاوت ساخت. این تکنیک برنامه نویسی به بهبود انعطاف پذیری و خوانایی کدهای نرم افزار کمک بسیار زیادی میکند. آیا شما تا کنون از این الگوی طراحی در نرم افزارهای خود استفاده کرده اید؟ پیاده سازی بهترین راه حلهای برنامه نویسی در طراحی نرم افزارها چقدر برای شما حائز اهمیت است؟
۳ دیدگاه
amirehsan۰۲ آذر ۱۴۰۰، ۰۶:۲۹
ممنون بابت مقاله خوبتون
Nazanin KarimiMoghaddam۰۳ آذر ۱۴۰۰، ۰۵:۴۳
ممنون که با ما همراه هستید.
F K۲۰ مهر ۱۳۹۸، ۲۲:۴۵
سلام
1 - توابع
$this->builder->produceBasement();
$this->builder->produceStructure();
$this->builder->produceRoof();
داخل توابع ()buildMinimalViableHouse و ()buildFullFeaturedHouse کلاس Director و همچنین توابع بعد از echo "Custom House:\n"; باید اصلاح شوند.
2 - پراپرتی $House کلاس به $house تغییر داده شود چون در مابقی توابع این کلاس با حرف کوچک شروع شده است و همچنین باید تابع getHouse() هم اصلاح گردد.