Builder Pattern in Java | الگوی بیلدر در جاوا
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
- Builder Pattern
الگوی طراحی سازندهای که فرآیند ساخت شیء را گامبهگام کرده و امکان ایجاد اشیاء تغییرناپذیر (immutable) را با پارامترهای اختیاری زیاد فراهم میکند. این الگو امنیت Telescoping Constructor را با خوانایی JavaBeans ترکیب میکند.
- Telescoping Constructor Pattern
روشی سنتی که در آن چندین سازنده با تعداد پارامترهای افزایشی تعریف میشوند و هر سازنده، سازنده کاملتر را صدا میزند. این شیوه با افزایش پارامترها به شدت ناخوانا و غیرقابل توسعه میشود.
- JavaBeans Pattern
رویکردی که در آن یک شیء با سازنده پیشفرض ساخته شده و سپس با متدهای setter مقداردهی میشود. این روش وضعیت ناسازگار موقت ایجاد کرده و امکان ساخت شیء تغییرناپذیر را از بین میبرد.
- Fluent API
روشی در طراحی متدها که در آن هر متد مرجع خود شیء (this) را بازمیگرداند تا بتوان فراخوانی متدها را به صورت زنجیرهای (method chaining) متصل کرد.
- Invariant Check
بررسی صحت شروط منطقی حاکم بر مقادیر فیلدها که باید همواره برقرار باشند، بهویژه روابطی که صحت یک پارامتر را به مقدار پارامتر دیگر وابسته میکند.
- Simulated Self-Type Idiom
راهکاری در جاوا برای شبیهسازی نوع بازگشتی شیء جاری در کلاسهای والد، که با کمک یک پارامتر جنریک بازگشتی (recursive type parameter) و متد انتزاعی self پیادهسازی میشود تا زنجیره متدها در فرزندان نیازی به تبدیل نوع (cast) نداشته باشند.
- Covariant Return Typing
قابلیتی در جاوا که به متد بازنویسیشده در زیرکلاس اجازه میدهد نوع بازگشتی تخصصیتری نسبت به متد کلاس والد برگرداند. این ویژگی باعث میشود متد build زیرکلاس مستقیماً نمونه همان کلاس را بازگرداند.
موارد مصاحبه ای
- چرا با وجود سازندهها و متدهای کارخانهای استاتیک به الگوی Builder نیاز داریم؟
سازندهها و static factory methods زمانی که تعداد پارامترهای اختیاری بالا میرود مقیاسپذیر نیستند و خوانایی کد در سمت کلاینت به شدت افت میکند.
- تفاوت ایمنی Builder نسبت به الگوی JavaBeans چیست؟
در الگوی JavaBeans شیء در طول فراخوانیهای متعدد متدهای setter در وضعیتی ناپایدار و ناسازگار (inconsistent state) قرار دارد و نمیتوان آن را immutable کرد، اما در Builder شیء نهایی پس از اعتبارسنجی کامل و به صورت یکباره ساخته میشود.
- اعتبارسنجی پارامترها در الگوی Builder در چه مراحلی انجام میشود؟
اعتبارسنجی مقادیر منفرد بلافاصله در متدهای setter بیلدر انجام میشود تا خطای اولیه سریع مشخص شود؛ اما اعتبارسنجی شروط ناوردا (invariants) که چند پارامتر را درگیر میکنند، درون متد build و قبل از ساخت نمونه نهایی بررسی میشود و در صورت نقض، استثنای IllegalArgumentException پرتاب میگردد.
- مزیت Builder در برخورد با پارامترهای با طول متغیر (varargs) نسبت به سازندهها چیست؟
سازندهها تنها میتوانند یک پارامتر varargs آن هم به عنوان آخرین پارامتر داشته باشند، اما در Builder میتوان متدهای متعددی با قابلیت دریافت varargs تعریف کرد یا با چندین بار فراخوانی یک متد، دادهها را داخل یک فیلد یا مجموعه تجمیع نمود.
- معایب استفاده از الگوی Builder چیست؟
هزینه جزئی ساخت شیء میانی بیلدر در حافظه (که در شرایط با کارایی بسیار بحرانی اهمیت مییابد) و افزایش حجم کدنویسی اولیه (verbosity)؛ به همین دلیل این الگو معمولاً برای کلاسهایی با ۴ پارامتر یا بیشتر توصیه میشود.
- تکنیک Covariant Return Typing چه کمکی به بیلدرهای سلسلهمراتبی میکند؟
این ویژگی باعث میشود که کلاینت بدون نیاز به تبدیل نوع دستی (type casting)، مستقیماً به متدها و نمونههای اختصاصی زیرکلاس دسترسی داشته باشد.
سناریو کاربردی
کلاس برچسب حقایق تغذیهای (NutritionFacts) دارای دو پارامتر اجباری اندازه هر وعده و تعداد وعده در بسته است، اما بیش از ۲۰ پارامتر اختیاری مانند چربی کل، چربی اشباع، سدیم و کربوهیدرات دارد. اغلب مواد غذایی تنها برای چند مورد از این فیلدها مقدار دارند.
با استفاده از الگوی Builder، فیلدهای اجباری مستقیماً به سازنده بیلدر پاس داده میشوند و فیلدهای اختیاری از طریق متدهای زنجیرهای با مقادیر پیشفرض تنظیم میگردند:
ابتدا کلاینت شیء NutritionFacts.Builder را با مقادیر اجباری فراخوانی کرده، سپس متدهای زنجیرهای مانند calories(100) و sodium(35) را فراخوانی میکند و در نهایت با اجرای متد build، شیء نهایی و تغییرناپذیر تولید میشود. این شیوه رفتاری شبیه به پارامترهای نامدار در زبانهایی نظیر پایتون و اسکالا ایجاد میکند.
در سناریوی سلسلهمراتب کلاسها مانند انواع پیتزا (Pizza به عنوان کلاس پایه، و NyPizza و Calzone به عنوان زیرکلاسها)، هر کلاس بیلدر موازی خود را دارد و پیادهسازی تکنیک self و covariant return typing اجازه میدهد ویژگیهای اختصاصی هر پیتزا بدون شکستن زنجیره متدها ثبت شوند.
بیشتر بدانید
- پیشبینی توسعه در آینده
اگر کلاسی در ابتدا پارامترهای کمی دارد اما احتمال اضافه شدن پارامترهای اختیاری در آینده وجود دارد، بهتر است از همان ابتدا با Builder طراحی شود؛ زیرا جایگزینی آن در آینده باعث باقی ماندن سازندههای منسوخ و ناهماهنگ در کدبیس میشود.
- بازاستفاده از شیء بیلدر
یک شیء بیلدر میتواند منعطف باشد و چندین بار برای ساخت چندین شیء مورد استفاده قرار گیرد، یا در بین فراخوانیها برخی فیلدهای آن بازتنظیم شوند.
- تولید خودکار در IntelliJ
در محیط IntelliJ IDEA میتوان با استفاده از قابلیتهای تولید خودکار کد یا ابزارهایی نظیر کتابخانه Lombok و استفاده از انوتیشن @Builder، کدهای تکراری مربوط به الگوی بیلدر را بدون نیاز به پیادهسازی دستی ایجاد کرد.
