طـــراح دکــــور (Decorator)
همچنین بh عنوان پوشش یا بستهبندی شناخته شده میشود.
هـــدف
الگوی دیزاین دیکوریتور الگویی ساختاری است که به شما اجازه میدهد رفتارهای جدیدی را به اشیاء اضافه کنید با قرار دادن این اشیاء درون اشیاء پوششدهندهی ویژهای که شامل رفتارهای مورد نظر هستند.
مـسئـــلـه
چه کار دشواری میتواند باشد؟ شما کلاس Notifier را گسترش دادید و متدهای اطلاعرسانی اضافی را در زیر کلاسهای جدید قرار دادید. اکنون مشتری باید کلاس اطلاعرسانی مورد نظر را نمونهسازی کند و از آن برای تمام اطلاعرسانیهای بعدی استفاده کند.
اما سپس کسی به طور معقول از شما پرسید: “چرا نمیتوانید از چندین نوع اطلاعرسانی به طور همزمان استفاده کنید؟ اگر خانه شما آتش گرفت، احتمالاً میخواهید از طریق هر کانال مطلع شوید.”
شما سعی کردید با ایجاد زیر کلاسهای خاصی که چندین روش اطلاعرسانی را در یک کلاس ترکیب میکردند، به این مشکل رسیدگی کنید. با این حال، به سرعت مشخص شد که این رویکرد کد را به شدت متورم میکند، نه تنها کد کتابخانه بلکه کد مشتری را نیز.
باید روشی دیگری برای ساختاردهی کلاسهای اطلاعرسانی پیدا کنید تا تعداد آنها به طور تصادفی رکورد گینس را نشکند.
راهــکــــار
گسترش یک کلاس اولین چیزی است که به ذهن میرسد وقتی نیاز دارید رفتار یک شیء را تغییر دهید. با این حال، وراثت چندین هشدار جدی دارد که باید از آن آگاه باشید.
وراثت استاتیک است. شما نمیتوانید رفتار یک شیء موجود را در زمان اجرا تغییر دهید. شما فقط میتوانید کل شیء را با شی دیگری که از یک زیر کلاس متفاوت ایجاد شده است جایگزین کنید.
زیر کلاسها فقط میتوانند یک کلاس والد داشته باشند. در اکثر زبانها، وراثت اجازه نمیدهد که یک کلاس رفتارهای چندین کلاس را به طور همزمان به ارث ببرد.
یکی از راههای غلبه بر این هشدارها استفاده از ترکیب یا جمعآوری به جای وراثت است. هر دو جایگزین تقریباً به یک روش کار میکنند: یک شی به شی دیگری ارجاع میدهد و برخی کارها را به آن واگذار میکند، در حالی که با وراثت، خود شی قادر به انجام آن کار است و رفتار را از کلاس والد خود به ارث میبرد.
با این رویکرد جدید، میتوانید به راحتی شی “کمکی” مرتبط را با دیگری جایگزین کنید و رفتار ظرف را در زمان اجرا تغییر دهید. یک شی میتواند از رفتار کلاسهای مختلف استفاده کند، با داشتن مراجع به چندین شی و واگذاری انواع کارها به آنها. ترکیب/جمعآوری اصل اصلی بسیاری از الگوهای طراحی از جمله دیکوریتور است. با این حال، بیایید به بحث الگو بازگردیم.
“پوشش” یا “بستهبندی” نام مستعار جایگزین برای الگوی دیکوریتور است که به وضوح ایده اصلی الگو را بیان میکند. یک پوشش شیئی است که میتواند با یک شی هدف مرتبط شود. پوشش شامل همان مجموعه متدهای هدف است و تمام درخواستهایی را که دریافت میکند به آن واگذار میکند. با این حال، پوشش ممکن است نتیجه را با انجام کاری قبل یا بعد از پاس دادن درخواست به هدف تغییر دهد.
چه زمانی یک پوشش ساده به یک دیکوریتور واقعی تبدیل میشود؟ همانطور که گفتم، پوشش رابط یکسانی با شی پوشیده شده پیادهسازی میکند. به همین دلیل از دیدگاه مشتری این اشیاء یکسان هستند. فیلد مرجع پوشش را طوری تنظیم کنید که هر شیئی که از آن رابط پیروی میکند را بپذیرد. این به شما امکان میدهد یک شی را در چندین پوشش قرار دهید و رفتار ترکیبی همه پوششها را به آن اضافه کنید.
در مثال اطلاعرسانی ما، بیایید رفتار سادهی اطلاعرسانی ایمیل را در کلاس پایه Notifier باقی بگذاریم، اما تمام روشهای اطلاعرسانی دیگر را به دیکوریتورها تبدیل کنیم.
کد مشتری باید یک شیء اطلاعرسان پایه را در مجموعهای از دیکوریتورها که با ترجیحات مشتری مطابقت دارد، بپیچاند. اشیاء حاصل به صورت یک پشته ساختاردهی خواهند شد.
آخرین دیکوریتور در پشته، شیئی خواهد بود که مشتری واقعاً با آن کار میکند. از آنجایی که همه دیکوریتورها همان رابط را با اطلاعرسان پایه پیادهسازی میکنند، بقیه کد مشتری اهمیتی نمیدهد که با شیء اطلاعرسان “خالص” یا شیء دیکوریت شده کار میکند.
ما میتوانیم همین رویکرد را برای رفتارهای دیگر مانند فرمتبندی پیامها یا ترکیب لیست گیرندگان اعمال کنیم. مشتری میتواند شیء را با هر دیکوریتور سفارشی تزئین کند، تا زمانی که از همان رابط با بقیه پیروی کند.
پوشیدن لباس مثالی از استفاده از دیکوریتورها است. وقتی سردتان است، خود را با یک سویشرت میپوشانید. اگر با سویشرت هم سردتان است، میتوانید یک ژاکت روی آن بپوشید. اگر باران میبارد، میتوانید یک بارانی بپوشید. همه این لباسها “رفتار پایه” شما را گسترش میدهند اما بخشی از شما نیستند و میتوانید هر زمان که به آنها نیازی ندارید، هر کدام را به راحتی درآورید.