الگوی طراحی Builder یک الگوی طراحی آفرینشی(Creational) است که به شما این امکان را می دهد تا ساخت اشیا پیچیده را به مراحل ساده و گام به گام تبدیل کنید. این الگو به شما اجازه میدهد انواع مختلف و نمایش های مختلفی از یک شی را با استفاده از همان کد ساختاری یکسان تولید کنید.
[divider]
بدون الگوی طراحی Builder با چه مشکلی روبرو میشویم؟
یک موجودیت پیچیده را تصور کنید که برای ایجاد نیاز به تعداد زیادی مراحل پشتسرهم و تنظیمات زیاد و حتی اشیاء تو در تو دارد. چنین موجود پیچیدهای برای ایجاد، نیاز به یک Constructor هیولا با تعداد بسیار زیادی پارامتر ورودی است. نمونههای ساخته شده از این موجودیت میتوانند تفاوتهای زیادی با هم داشته باشند که به اینصورت منطق و روال ایجاد این نمونهها در همان Constructor و در بین هزاران خط کد، دفن خواهد شد. یا حتی بدتر؛ این منطق و روال در همه جای کد پراکنده خواهد شد.
به عنوان مثال، بیایید در مورد چگونگی ایجاد یک شیء پیچیده، مثلا ساخت خانه فکر کنیم. برای ساختن یک خانه ساده، باید چهار دیوار و یک کف احداث کنید، یک درب نصب کنید، یک جفت پنجره را نصب کنید و یک سقف بسازید. اما اگر می خواهید خانهای بزرگتر، روشن تر، دارای حیاط خلوت و سایر وسایل خوبی مانند سیستم گرمایش، لوله کشی و سیم کشی برق باشید، چه می کنید؟
ساده ترین راه حل، گسترش کلاس پایه خانه و ایجاد مجموعهای از زیر کلاسها برای پوشش دادن تمام ترکیبات پارامترها است. اما سرانجام با تعداد قابل توجهی از زیر کلاس ها روبرو خواهید شد. هر پارامتر جدید، مانند سبک ایوان، نیاز به رشد بیشتر این سلسله مراتب دارد.
اینطور که به نظر میرسد، اولین تلاشمان برای مدیریت این همه پیچیدگی با شکست روبرو شد. اما نا امید نشوید! یک رویکرد دیگر وجود دارد که شامل زیر کلاسها نمی شود. شما می توانید یک سازنده غول پیکر درست کنید که در کلاس خانه پایه، با تمام پارامترهای ممکن، کنترل شیء خانه را در دست بگیرد. در حالی که این رویکرد در واقع نیاز به زیر کلاسها را از بین می برد، مشکل دیگری را ایجاد می کند.
مشکل رویکرد دوم ما این است که در بیشتر موارد، بیشتر پارامترها مورد استفاده قرار نمی گیرند و فراخوانی سازنده با کلی پارامتر خالی، کدنویسی ما را بسیار زشت میکند. به عنوان مثال، تنها بخشی از خانهها دارای استخر شنا هستند؛ بنابراین پارامترهای مربوط به استخرهای شنا ۹ بار از ۱۰ مورد بی فایده هستند و با مقادیر تهی(null) مقدار دهی خواهند شد.
[divider]
دیزاین پترن Builder چه مشکلی را حل میکند؟
بعد از تست رویکردهایی که داشتیم، سراغ الگوی طراحی Builder میرویم که برای همین چالش طراحی شده است. قبلا در مقدمه الگوهای طراحی نرمافزار گفتیم که این الگوها حاصل تلاش و همفکری برنامهنویسان زیادی است و در مدت زمانی نسبتا طولانی کشف شدهاند. پس به نظر میرسد قابل اعتماد باشند و مشکلات ما را به خوبی برطرف کنند. الگوی طراحی Builder پیشنهاد می کند که کد ساخت شی یا همان Constructor را از کلاس خود خارج کنیم و آن را به اشیاء جداگانه بنام سازندگان منتقل کنیم.
این الگو، ساخت شی را به مجموعه ای از مراحل (buildWalls ، buildDoor و غیره) سازماندهی میکند. برای ایجاد یک شی(مثلا خانه)، یک سری از این مراحل را روی یک شیء سازنده اجرا می کنیم. نکته مهم این است که ما نیازی به تماس با همه مراحل نداریم یعنی نیازی نیست تمام این مراحل را در کدمان فراخوانی کنیم. ما میتوانیم فقط آن مراحلی را که برای تولید یک پیکربندی خاص از یک شی نیاز هست را فراخوانی کنیم. شاید به نظر عجیب و کمی گنگ باشد اما به خواندن این مطلب ادامه دهید! بزودی این ابهامات برطرف خواهد شد.
در هنگام ساختن بازنمایی های مختلف محصول ممکن است برخی از مراحل ساخت و ساز، نیاز به اجرای متفاوتی داشته باشد. به عنوان مثال، دیوارهای یک کابین ممکن است از چوب ساخته شود، اما دیوارهای قلعه باید با سنگ ساخته شوند.
در این حالت، شما می توانید چندین کلاس سازنده مختلف ایجاد کنید که همان مجموعه مراحل ساخت را اجرا می کند، اما به روشی متفاوت. سپس می توانید از این سازندگان در مراحل ساخت (یعنی مجموعه ای از فراخوانیهای سفارش داده شده به مراحل ساخت) برای تولید انواع مختلف اشیاء استفاده کنید.
به عنوان مثال، سه سازنده مختلف را در نظر بگیرید که خانه میسازند. یک سازنده همه چیز را از چوب و شیشه می سازد، دومی همه چیز را با سنگ و آهن و سومی که از طلا و الماس استفاده می کند. با فراخوانی همان مجموعه مراحل، یک خانه معمولی از سازنده اول ، یک قلعه کوچک از دومی و یک کاخ از سومی دریافت می کنید. با این حال، این تصور تنها در صورتی کار می کند که کد ما که مراحل ساخت را فرا میخواند قادر به تعامل با سازندگان با استفاده از یک رابط مشترک باشد.
کارگردان(Director)
شما می توانید باز هم فراتر رفته و یک سری از فراخوانیها به مراحل سازنده که برای ساخت محصول مورد استفاده قرار میگیرد، در یک کلاس جداگانه به نام کارگردان یا همان Director قرار دهید. کلاس Director ترتیب اجرای مراحل ساخت را مشخص می کند، در حالی که سازنده اجرای آن مراحل را بر عهده دارد.
داشتن کلاس Director در برنامه شما الزاما ضروری نیست. همیشه می توانید مراحل ساخت را به ترتیب خاصی مستقیماً از طریق کد فراخوانی کنید. با این حال، کلاس Director ممکن است مکان مناسبی برای قرار دادن کارهای مختلف ساخت و ساز باشد تا بتوانید از آنها در برنامه خود استفادهی مجدد کنید.
علاوه بر این، کلاس Director جزئیات ساخت محصول را از کد اصلی برنامه پنهان می کند. مشتری فقط باید یک سازنده را با یک Director مرتبط کند، ساخت و ساز را با Director راه اندازی کند و از سازنده نتیجه را بگیرد.
[divider]
ساختار الگوی طراحی Builder
رابط Builder مراحل ساخت محصول را که مشترک برای همه سازندگان است، اعلام می کند. در واقع متدهایی که هر سازندهای باید آنها را داشته باشد تعیین میکند.
Concrete Builders اقدامات مختلفی از مراحل ساخت را ارائه می دهند. در واقع شامل همان متدهایی هستند که رابط Builder وجود آنها را لازم دانسته بود. از طرفی Concrete Builders ممکن است محصولاتی تولید کنند که از رابط رایج پیروی نکنند زیرا میتوانند شامل متدهای بیشتری برای ساخت و ساز باشند که رابط Builder آنها را نداشته باشد.
محصولات در واقع اشیاء نتیجه هستند. محصولاتی که توسط سازندگان مختلف ساخته شده اند مجبور نیستند متعلق به سلسله مراتب یا رابط کلاس یکسانی باشند. پس آزادند هر طور که لازم است باشند. مثلا قلعه بجای درب میتواند دروازه داشته باشد و برعکس خانههای معمول، تعداد این دروازهها میتواند زیاد باشد.
کلاس Director ترتیب استفاده از مراحل ساخت را تعیین می کند، بنابراین می توانید از تنظیمات خاص محصولات برای ایجاد و استفاده مجدد بهره ببرید. مثلا برای ساخت یک خانه ساده مراحل کمتری را مورد استفاده قرار دهید و برای ساخت خانههای پیچیدهتر و یا دیگر انواع خانه مانند قصر و قلعه، مراحل پیچیدهتری را طی کنید. با اینکار پیچیدگی ساخت انواع خانه را میتوانید در کلاس Director یک بار برای همیشه بنویسید و هر کجای برنامه که لازم بود، با فراخوانی Director و تعیین نوع خانه، Object مورد نیاز را تحویل بگیرید.
کد اصلی برنامه باید یک Object از یکی از کلاسهای Builder را با Director مرتبط کند. معمولاً این کار فقط یک بار از طریق پارامترهای Constructor مربوط به کلاس Director انجام می شود. سپس Director برای ساخت و سازهای بعدی از آن شیء استفاده می کند. با این وجود، در کلاس Director یک متد برای جایگزین کردن نوع سازنده(changeBuilder) برای زمانی وجود دارد که برنامه ما قصد دارد روش ساخت جدیدی را به Director مرتبط کند. در این حالت، هر بار که می خواهید چیزی را با Director تولید کنید، می توانید از سازندهی دیگری استفاده کنید. مثلا بجای ساخت خانه به کمک Builder1 کار ساخت را با Builder2 انجام دهید.
موفق و پیروز باشید…