الگوی Decorator یکی از پرکاربردترین الگوهای طراحی، تحت الگوی های ساختاری می باشد و مبتنی بر اصل Open-Closed است.
اصل Open-Closed به طور خلاصه پیشنهاد می کند کلاس ها برای توسعه باید باز و برای اصلاح باید بسته شوند.
الگوی decorator به کاربر اجازه می دهد بدون تغییر در ساختار یک شی موجود، به آن عملکرد جدیدی را اضافه کند.
با یک مثال ساده الگوی decorator را بررسی خواهیم کرد.
کار خود را با یک interface با نام ICar که توسط کلاس های Deluxe ،Economy و Luxury پیاده سازی شده است شروع می کنیم.
public interface ICar { string GetDescription(); double GetCost(); } public class EconomyCar : ICar { public string GetDescription() { return "Economy Car" } public double GetCost() { return 450000.0; } } public class DeluxCar : ICar { public string GetDescription() { return "Delux Car" } public double GetCost() { return 750000.0; } } public class LuxuryCar : ICar { public string GetDescription() { return "Luxury Car" } public double GetCost() { return 1000000.0; } }
تا اینجای کار کدها را با رعایت اصول single repository principle و open closed principle توسعه دادیم. اما در نظر بگیرید قصد داریم به خودروها پکیج های جانبی و اختیاری اضافه کنیم. این موضوع را چگونه طراحی کنیم؟ آیا مانند روال بالا به ازای هر پکیج یک پیاده سازی داشته باشیم؟
public class EconomyCarWithBasicAccessories: ICar { .. .. } public class DeluxCarWithBasicAccessories: ICar { .. .. } public class LuxuryCarWithBasicAccessories: ICar { .. .. } .. .. .. .. public class EconomyCarWithSportAccessories: ICar { .. .. } public class DeluxCarWitSportAccessories: ICar { .. .. } public class LuxuryCarWithSportAccessories: ICar { .. .. }
بله، ما می توانیم کلاس های خود را مانند بالا ایجاد کنیم. اما هر زمانی که بخواهیم یک پکیج جانبی جدید اضافه کنیم، باید به ازای آن، چهار کلاس جدید نیز ایجاد کنیم. به این دلیل پس از مدتی حجم کدها بسیار زیاد و مدیریت آن ها سخت می شود.
برای جلوگیری از این اتفاق، ما می توانیم از الگوی decorator کمک بگیریم. با استفاده از این الگو، هر زمان که یک پکیج جانبی جدید اضافه شود، نیاز به ایجاد کلاس های فرعی اضافی نخواهیم داشت.
در decorator ما یک abstract و تعدادی کلاس پیاده سازی شده داریم و همچنین خواسته های دیگری داریم که این خواسته ها باید بتوانند کلاس من را گسترش دهند و من مجبور نشوم کلاس خودم را تغییر دهم.
بنابراین الگوی خود را اینگونه پیاده سازی می کنیم. ابتدا یک کلاس decorator ایجاد می کنیم.
public abstract class CarAccessoriesDecorator : ICar { private ICar _car; public CarAccessoriesDecorator(ICar aCar) { _car = aCar; } public virtual string GetDescription() { return _car.GetDescription(); } public virtual double GetCost() { return _car.GetCost(); } }
سپس هر پکیج جانبی جدید که اضافه می شود کلاس decorator ما را پیاده سازی می کند.
public class BasicAccessories : CarAccessoriesDecorator { public BasicAccessories(ICar aCar) : base(aCar) { } public override string GetDescription() { return base.GetDescription() + ",Basic Accessories Package" } public override double GetCost() { return base.GetCost() + 2000.0; } } public class AdvancedAccessories : CarAccessoriesDecorator { public AdvancedAccessories(ICar aCar) : base(aCar) { } public override string GetDescription() { return base.GetDescription() + ",Advanced Accessories Package" } public override double GetCost() { return base.GetCost() + 10000.0; } } public class SportsAccessories : CarAccessoriesDecorator { public SportsAccessories(ICar aCar) : base(aCar) { } public override string GetDescription() { return base.GetDescription() + ",Sports Accessories Package" } public override double GetCost() { return base.GetCost() + 15000.0; } }
در نمودار زیر می توانید طرحی از کدهای بالا را ملاحظه کنید.
هر زمان که نیاز به ایجاد یک پکیج جانبی جدید داریم، تنها یک کلاس جدید اضافه می کنیم.
در نهایت می توانیم به این صورت از کدهای خود استفاده کنیم.
static void Main(string[] args) { ICar objCar = new EconomyCar(); CarAccessoriesDecorator objAccessoriesDecorator = new BasicAccessories(objCar); objAccessoriesDecorator = new AdvancedAccessories(objAccessoriesDecorator); Console.Write("Car Detials: " + objAccessoriesDecorator.GetDescription()); Console.WriteLine("\n\n"); Console.Write("Total Price: " + objAccessoriesDecorator.GetCost()); Console.Read(); }
یک نمونه از EcomomyCar ایجاد کردیم و عملکردهای جانبی اضافه را یکی پس از دیگری به آن اضافه کردیم.
پایان