Decorator یکی از الگوهای طراحی ساختاری (Structural Design Pattern) مطرح شده در کتاب معروف Design Pattern به تالیف GoF می باشد. در این مقاله به بررسی این الگو و کاربردهای آن می پردازیم.
- هدف الگوی Decorator
هدف از پیاده سازی الگوی Decorator اضافه کردن یک وضعیت و یا یک رفتار (Behavior) به یک کلاس بدون تغییر دادن آن می باشد. این عمل می تواند کاملا به صورت داینامیک انجام شود. این دو مشخصه (تغییر نکردن کلاس فعلی و داینامیک بودن)، Decorator را تبدیل به یکی از پرکاربردترین الگوهای طراحی شیء گرا کرده است.
- طرح مساله و مثال ساده
با یک مثال بسیار ساده به سراغ مشکل و سپس راه حل آن می رویم. کلاس های زیر را در نظر بگیرید :
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface IComponent { string GetInfo(); } public class Component : IComponent { public string GetInfo() { return "simple text"; } } |
یک اینترفیس به نام IComponent که توسط کلاس Component پیاده سازی شده است و متن ساده ای را برمی گرداند. متن برگردانده شده نیز بر روی صفحه چاپ می شود :
1 2 3 4 5 6 7 |
static void Main(string[] args) { IComponent component = new Component(); Console.WriteLine(component.GetInfo()); Console.ReadLine(); } |
اکنون فرض کنید نیاز دارید تا متن ساده را با حروف بزرگ (Uppercase) به کلاس استفاده کننده (در این مثال متد Main) برگردانید. از آنجا که (فرضا) کلاس Component در قست های دیگری از برنامه نیز استفاده شده است، تغییر آن برای شما مشکل بوده و یا مقدور نیست. اما چگونه می توانید رفتار جدید را بدون تغییر کلاس فعلی به آن اضافه کنید؟ یکی از راه حل هایی که شاید به ذهنتان رسیده، ارث بری از کلاس Component و Override کردن متد GetInfo باشد. اما ارث بری راه حلی تنها محدود به کلاس Component خواهد شد. برای مثال در صورتی که پیاده سازی دیگری از کلاس IComponent انجام شود، باز با مشکل فوق مواجه خواهیم شد. همچنین ارث بری در زمان اجرا (Runtime) قابل تغییر نبوده و محدود می باشد. از آن سو در صورتی که کلاس Component به صورت Sealed تعریف شده باشد، امکان ارث بری وجود ندارد.
- راه حل Decorator
نکته ی اصلی و کلیدی در پیاده سازی الگوی Decorator ارث بری از Interface اصلی و همچنین داشتن یک نمونه از آن در کلاس Decorator به صورت همزمان می باشد. به کد زیر دقت کنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class ComponentUppercaseDecorator : IComponent { private IComponent component; public ComponentUppercaseDecorator(IComponent component) { this.component = component; } public string GetInfo() { var info = component.GetInfo(); return info.ToUpper(); } } |
کلاسی با نام ComponentUppercaseDecorator تعریف شده است که هم IComponent را پیاده سازی کرده و هم شامل یک نمونه از آن می شود. سپس در پیاده سازی متد GetInfo از آن نمونه برای گرفتن متن استفاده کرده، و پس از Uppercase کردن آن، مقدار برگردانده می شود. همانطور که ملاحظه میکنید کلاس Component کوچکترین تغییری نمیکند. حال در کلاس Main از کلاس نوشته شده استفاده میکنیم :
1 2 3 4 5 6 7 8 |
static void Main(string[] args) { IComponent component = new Component(); IComponent decoratComponent = new ComponentUppercaseDecorator(component); Console.WriteLine(decoratComponent.GetInfo()); Console.ReadLine(); } |
- نکات مهم در پیاده سازی Decorator
- کلاس اصلی (در مثال بالا Component) هیچ تغییری نمی کند و از وجود کلاس Decorator بی اطلاع است.
- Decorator های مختلف از یکدیگر مستقل بوده و به همدیگر وابستگی ندارند.
- توصیه می شود کلاس Decorator به Interface وابسته باشد و نه به کلاس پیاده سازی شده. (مانند این مثال که Decorator به IComponent وابستگی دارد و نه به Component)
- مثال های کاربردی از الگوی Decorator
یادگیری Design Pattern ها نیازمند مطالعه و بررسی مثال های زیاد است. جهت یادگیری بهتر این الگو، پیشنهاد می شود منابع زیر را مطالعه کنید :
- کتاب C# Design Patterns – صفحه ۱۶ مثال Photo Decorator
- کتاب Professional ASP.NET Design Patterns – صفحه ی ۱۰۰ مثال ProductDecorator
- کتاب Head First Design Patterns – صفحه ی ۱۰۰ بخش Real World Decorators: Java IO (در این بخش به بررسی پکیج java.io پرداخته شده که در آن از الگوی Decorator جهت ساخت کلاس های مختلف Stream استفاده شده است. همچنین در ادامه به پیاده سازی یک Stream جدید می پردازد. مطالعه ی این بخش برای یادگیری کاربردی این الگو به شدت توصیه می شود. همچنین می توانید به مطالعه ی ساختار System.IO.Stream در .NET Framework بپردازید زیرا در این Namespace نیز از الگوی Decorator بسیار استفاده شده است.)
باسلام
من میخوام بدونم که از هرکدوم از الگوهای لایه دسترسی به داده که در ادامه ذکر شده چه موقع استفاده میشه-یعنی راهی برای تشخیص الگویی که میخوام در برنامه استفاده کنم لازم دارم-هرالگو برای چه شرایطی مناسب هست.متشکرم
-unit of work
-marker pattern unit of work
-lazy loading , proxy
-Identity map
-query object
-repository,generic repository
سلام
الگوهای که ذکر فرمودید مثل تمام الگوهای دیگر برای رفع یک مشکل مطرح شده اند. یک طراح بسته به نیاز پروژه می تواند از هریک از الگوهای فوق در جای مناسب استفاده نماید. اینکه هر الگو در چه شرایطی مناسب است، تنها با مطالعه ی دقیق آنها برایتان مشخص خواهد شد. بعضی از الگوها و تکنیک های مطرح شده (مثل Lazy Loading) امروزه توسط ORM ها پیاده سازی شده اند و نیاز به پیاده سازی توسط برنامه نویس ها نیست. فقط کافی است از آنها به طور صحیح استفاده گردد.
منابع بسیار زیادی در مورد هر یک از الگوهای فوق وجود دارد، می تونید سرچ بفرمایید.
عالی بود . سپاسگزارم از زحماتتون
خواهش میکنم، لطف دارید