اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
سه دقیقه
این بار مثال را با شیرینی و کیک پیش میبریم.
در کد فوق فرض کردهام که شما میخواهید محصول خودتان را بفروشید و برای آن یک متد GetPrice نیز گذاشتهام. خوب در ابتدا واسطی تعریف شده و متدهای Bake و GetPrice اعلام شدهاند. سپس کلاسهای Cake و Bread پیاده سازیهای خودشان را انجام دادند.
در کد فوق یک کلاس انتزاعی ایجاد و متدهای پختن و قیمت را پیاده سازی کردیم؛ همچنین کلاسهای Type1 و Type2 را که من فرض کردم کلاسهایی هستند برای اضافه کردن مخلفات به کیک و نان. در این کلاسها در متد سازنده، یک شیء از نوع IBakery میگیریم که در واقع این شیء یا از نوع Cake هست یا از نوع Bread و مشخص میکند روی کیک میخواهیم مخلفاتی را اضافه کنیم یا بر روی نان. کلاس Type1 روش پخت و قیمت را از کلاس انتزاعی پیروی میکند، ولی کلاس Type2 روش پخت خودش را دارد.
ابتدا یک کیک را پختیم در ادامه Type1 را به آن اضافه کردیم که این باعث میشود قیمتش هم زیاد شود و در نهایت Type2 را هم به کیک اضافه کردیم و حالا کیک ما آماده است.
فرض کنید شما قصد پخت کیک و نان را دارید. طبیعی است که برای اینکار یک واسط را تعریف کرده و عمل «پختن» را در آن اعلام میکنید تا هر کلاسی که قصد پیاده سازی این واسط را داشت، «پختن» را انجام دهد. در ادامه یک کلاس بنام کیک ایجاد خواهید کرد و شروع به پخت آن میکنید.
خوب احتمالا الان کیک آمادهاست و میتوانید آنرا میل کنید! ولی یک سؤال. تکلیف شخصی که کیک با روکش کاکائو دوست دارد و شمایی که کیک با روکش میوهای دوست دارید چیست؟ این را چطور در پخت اعمال کنیم؟ یا منی که نان کنجدی میخواهم و شمایی که نان برشتهی غیر کنجدی میخواهید چطور؟
احتمالا میخواهید سراغ ارث بری رفته و سناریوهای این چنینی را پیاده سازی کنید. ولی در مورد ارث بری، اگر کلاس sealed (NotInheritable) باشه چطور؟
احتمالا همین دو تا سؤال کافیاست تا در پاسخ بگوئیم، گرهی کار، با الگوی Decorator باز میشود و همین دو تا سؤال کافیاست تا اعلام کنیم که این الگو، از جمله الگوهای بسیار مهم و پرکاربرد است.
در ادامه سناریوی خود را با کد ذیل جلو میبریم:
public interface IBakery { string Bake(); double GetPrice(); } public class Cake: IBakery { public string Bake() { return "Cake baked"; } public double GetPrice() { return 2000; } } public class Bread: IBakery { public string Bake() { return "Bread baked"; } public double GetPrice() { return 100; } }
در ادامه باید مخلفاتی را که روی کیک و نان میتوان اضافه کرد، پیاده نمود.
public abstract class Decorator : IBakery { private readonly IBakery _bakery; protected string bake = "N/A"; protected double price = -1; protected Decorator(IBakery bakery) { _bakery= bakery; } public virtual string Bake() { return _bakery.Bake() + "/" + bake; } public double GetPrice() { return _bakery.GetPrice() + price; } } public class Type1 : Decorator { public Type1(IBakery bakery) : base(bakery) { bake= "Type 1"; price = 1; } } public class Type2 : Decorator { private const string bakeType = "special baked"; public Type2(IBakery bakery) : base(bakery) { name = "Type 2"; price = 2; } public override string Bake() { return base.Bake() + bakeType ; } }
با بررسی اجمالی در کدهای فوق مشخص میشود که هرگاه بخواهیم، میتوانیم رفتارها و الحاقات جدیدی را به کلاسهای Cake و Bread، اضافه کنیم؛ بدون آنکه کلاس اصلی آنها تغییر کند. حال شما شاید در پیاده سازی این الگو از کلاس انتزاعی Decorator هم استفاده نکنید.
با این حال شیوهی استفاده از این کدها هم بصورت زیر خواهد بود:
Cake cc1 = new Cake(); Console.WriteLine(cc1.Bake() + " ," + cc1.GetPrice()); Type1 cd1 = new Type1 (cc1); Console.WriteLine(cd1.Bake() + " ," + cd1.GetPrice()); Type2 cd2 = new Type2(cc1); Console.WriteLine(cd2.Bake() + " ," + cd2.GetPrice());