در این مقاله، ما قصد داریم که در مورد الگوی طراحی ساختاری دیگری در #C یعنی الگوی طراحی Decorator صحبت کنیم. ما قصد داریم نحوه پیاده سازی این الگو را در پروژه خود و اینکه با استفاده از این الگو، چه چیزی عاید ما میشود صحبت کنیم.
این مقاله، بخشی از مجموعه آموزشی زیر است:
- الگوی طراحی Builder و Fluent Builder
- اینترفیس Fluent Builder به همراه Generic بازگشتی
- Facated Builder
- متد Factory
- Singleton
- Adapter
- Composite
- Decorator
- Command
- Strategy
- Facade
سورس را میتوانید از این لینک دریافت کنید: الگوی طراحی Decorator – سورس کد.
این مقاله، شامل قسمتهای زیر میباشد:
شرح الگوی طراحی Decorator
یک Decorator، یک الگوی طراحی ساختاری است که این امکان را میدهد که با قرار دادن آبجکتها به داخل یک کلاس wrapper ویژه، رفتار آن آبجکتها را توسعه دهیم. الگوی طراحی Decorator در سی شارپ بسیار محبوب است زیرا به ما کمک می کند تا به صورت داینامیک، رفتارهایی را به آبجکتهای wrap شده اضافه کنیم.
ساختار این الگو، از یک طرف، شامل یک کلاس Component و یک کلاس Concrete Component و از طرف دیگر، شامل یک کلاس Decorator و یک کلاس Concrete Decorator است.
در نتیجه، چه زمانی باید از این الگو استفاده کنیم؟
خوب، زمانی که نیاز داریم رفتار اضافی به آبجکت خود اضافه کنیم، باید از این الگو استفاده کنیم. علاوه بر این، زمانی که استفاده از وراثت خیلی پیچیده است یا زمانی که اصلاً معنی ندارد، باید از این الگو استفاده کنیم (لایه های ارثی بیش از حد، اصرار برای اصلاح سلسله مراتب وراثت موجود با افزودن چند لایه اضافی و غیره).
ما می خواهیم طی یک مثال عملی ببینیم که همه این عناصر در داخل الگوی طراحی Decorator چگونه با یکدیگر کار می کنند، که باعث شود درک این الگو برای ما آسان تر شود.
پیاده سازی الگوی طراحی Decorator
تصور کنیم که یک اپلیکیشن ساده داریم که قیمت کل سفارش در فروشگاه ما را محاسبه میکند. در ضمن، تعدادی درخواستهای اضافی دیگری نیز داریم. اگر خریدار محصولات ما را در دوره پیش سفارش سفارش دهد، 10 درصد تخفیف می دهیم. پس با کلاس Component خود شروع میکنیم:
1 2 3 4 5 6 7 8 9 10 11 |
public abstract class OrderBase { protected List<Product> products = new List<Product> { new Product {Name = "Phone", Price = 587}, new Product {Name = "Tablet", Price = 800}, new Product {Name = "PC", Price = 1200} }; public abstract double CalculateTotalOrderPrice(); } |
کلاس Component شامل قابلیت هایی است که با سایر کلاس های Concrete Component به اشتراک گذاشته می شود. از این رو، اجازه دهید تا Concrete Component های مورد نظر را ایجاد کنیم:
1 2 3 4 5 6 7 8 |
public class RegularOrder : OrderBase { public override double CalculateTotalOrderPrice() { Console.WriteLine("Calculating the total price of a regular order"); return products.Sum(x => x.Price); } } |
1 2 3 4 5 6 7 8 |
public class Preorder : OrderBase { public override double CalculateTotalOrderPrice() { Console.WriteLine("Calculating the total price of a preorder."); return products.Sum(x => x.Price) * 0.9; } } |
این کد بسیار تمیز و قابل درک است. ما فقط متد abstract خود یعنی CalculateOrderPrice
را override کردیم و قیمت کل را محاسبه کردیم. بنابراین، در حال حاضر، می توانیم استفاده از این concrete component ها را شروع کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Program { static void Main(string[] args) { var regularOrder = new RegularOrder(); Console.WriteLine(regularOrder.CalculateTotalOrderPrice()); Console.WriteLine(); var preOrder = new PreOrder(); Console.WriteLine(preOrder.CalculateTotalOrderPrice()); Console.WriteLine(); } } |
این بدون هیچ مشکلی کار میکند.
اما اکنون، یک درخواست اضافی دریافت میکنیم تا به کاربران premium خود برای پیشسفارش ده درصد تخفیف اضافه بدهیم. البته، اینجا ما فقط میتوانیم کلاس Preorder را با یک عبارت if تغییر دهیم تا بررسی کنیم که آیا کاربر ما یک کاربر premium است یا خیر، اما این باعث میشود که اصل Open Closed شکسته شود. بنابراین برای پیاده سازی این درخواست اضافی، میخواهیم با یک کلاس Decorator که قرار است آبجکت OrderBase
ما را wrap کند شروع کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class OrderDecorator : OrderBase { protected OrderBase order; public OrderDecorator(OrderBase order) { this.order = order; } public override double CalculateTotalOrderPrice() { Console.WriteLine($"Calculating the total price in a decorator class"); return order.CalculateTotalOrderPrice(); } } |
حالا میتوانیم کلاس (Concrete Decorator)
PremiumPreorder
را پیاده سازی کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class PremiumPreorder : OrderDecorator { public PremiumPreorder(OrderBase order) : base(order) { } public override double CalculateTotalOrderPrice() { Console.WriteLine($"Calculating the total price in the {nameof(PremiumPreorder)} class."); var preOrderPrice = base.CalculateTotalOrderPrice(); Console.WriteLine("Adding additional discount to a preorder price"); return preOrderPrice * 0.9; } } |
در این کلاس، قیمت کل آبجکت OrderBase را محاسبه می کنیم، اما رفتار تخفیف اضافی را نیز در آن لحاظ می کنیم.
در آخر، میتوانیم کلاس Program.cs را تغییر دهیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Program { static void Main(string[] args) { var regularOrder = new RegularOrder(); Console.WriteLine(regularOrder.CalculateTotalOrderPrice()); Console.WriteLine(); var preOrder = new Preorder(); Console.WriteLine(preOrder.CalculateTotalOrderPrice()); Console.WriteLine(); var premiumPreorder = new PremiumPreorder(preOrder); Console.WriteLine(premiumPreorder.CalculateTotalOrderPrice()); } } |
همانطور که می بینیم، با آبجکتPremiumPreorder، ما در حال wrap کردن آبجکت preOrder هستیم که یک رفتار اضافی را به این آبجکت اضافه می کنیم:
حالا میتوانیم به وضوح ببینیم که چطور کلاس Decorator ما، آبجکت preorder را wrap کرده است.
بسیار عالی.
نتیجه گیری
در این مقاله، ما یاد گرفتیم که:
- الگوی طراحی Decorator چیست
- چه زمانی باید از این الگو استفاده کنیم
- جطور این الگو را در عمل پیاده سازی کنیم