آنچه باید در مورد Design Patterns بدونیم

الگوهای طراحی (Design Patterns) در زبان‌های برنامه‌نویسی به عنوان رویکردهای مشترک و بهینه برای حل مشکلات معمول در طراحی نرم‌افزارها شناخته می‌شوند. این الگوها توسط توسعه‌دهندگان نرم‌افزار در طول زمان شناسایی شده‌اند و به عنوان روش‌های مورد تایید برای ایجاد ساختارها و تعاملات میان اجزای یک سیستم نرم‌افزاری مورد استفاده قرار می‌گیرند. الگوهای طراحی به توسعه‌دهندگان کمک می‌کنند تا کد قابل نگهداری، قابلیت افزایش‌پذیری و قابلیت تغییرپذیری داشته باشد. در ادامه به توضیح و معرفی معروف ترین Design Pattern ها میپردازیم ، نمونه ها با زبان C# بیان میشوند . Singleton Pattern: این الگو برای اطمینان از اینکه یک کلاس تنها یک نمونه دارد و دسترسی به آن نمونه را به صورت گلوبال ارائه می‌دهد، استفاده می‌شود. این الگو به صورت خاص در مواقعی که نیاز به ایجاد یک نمونه از یک کلاس در سراسر برنامه داریم مفید است. Factory Method Pattern: این الگو به تولید نمونه‌های متنوع از یک کلاس برای یک خانواده کلاس‌ها کمک می‌کند. با استفاده از این الگو، تصمیم گیری در مورد اینکه کدام نوع از کلاس‌ها ایجاد شود به کلاس‌های فرزند تعیین می‌شود. Observer Pattern: این الگو برای پیاده‌سازی مکانیزم اطلاع‌رسانی از تغییرات در یک شیء به شیء‌های دیگر استفاده می‌شود. این الگو به تعامل بین اجزای مختلف نرم‌افزار کمک می‌کند و اجازه می‌دهد تا شیء‌ها بر اساس تغییرات در یکدیگر واکنش نشان دهند. Strategy Pattern: این الگو به تعیین یک مجموعه از الگوریتم‌ها و قابلیت تعویض آنها در زمان اجرا برای انجام یک وظیفه خاص کمک می‌کند. این الگو اجازه می‌دهد تا یک کلاس تغییرات در رفتار خود را در زمان اجرا اعمال کند بدون اینکه نیاز به تغییر در کد مشترک داشته باشد. Decorator Pattern: این الگو برای افزودن عملکرد به یک شیء به صورت پویا استفاده می‌شود. این الگو امکان می‌دهد تا بدون تغییر در ساختار کلاس اصلی، ویژگی‌ها و عملکردهای آن را توسعه دهیم.این الگوها تنها نمونه‌هایی از الگوهای طراحی در C# هستند و هنوز الگوهای دیگری نیز وجود دارند که برای حل مسائل خاصی طراحی شده‌اند. استفاده از الگوهای طراحی به توسعه‌دهندگان کمک می‌کند تا کد خود را سازماندهی کرده و مشکلات رایج در طراحی نرم‌افزارها را به بهترین نحو حل کنند.در ادامه توضیحات بیشتری در خصوص هر یک از Design Pattern های معرفی شده ارائه میکنیم .Singleton Pattern:الگوی Singleton یک الگوی طراحی در برنامه‌نویسی است که به تنهایی یک نمونه از یک کلاس را ایجاد می‌کند و اجازه نمی‌دهد که سایر کلاس‌ها به این نمونه دسترسی مستقیم داشته باشند. این الگو برای مواقعی مناسب است که فقط یک نمونه از یک کلاس در برنامه نیاز دارید، مثلاً برای مدیریت تنظیمات یا اتصال به پایگاه داده.در زیر، یک مثال از الگوی Singleton در C# نمایش داده شده است:public class Singleton { // این متغیر private و استاتیک برای ذخیره نمونه ایجاد شده از کلاس Singleton است. private static Singleton instance; // متغیرهای دیگر می‌توانند اضافه شوند. public string SomeData { get; set; } // کانستراکتور خصوصی تا از ایجاد نمونه‌های جدید جلوگیری کند. private Singleton() { SomeData = &quotInitial data&quot } // متد استاتیک برای ایجاد یک نمونه از کلاس Singleton. public static Singleton GetInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }ر این مثال، کلاس Singleton دارای یک متغیر استاتیک به نام instance است که نمونه‌ی واحد از کلاس را نگهداری می‌کند. کانستراکتور کلاس خصوصی است، بنابراین نمی‌توان از خارج از کلاس نمونه‌های دیگری از آن ایجاد کرد. متد GetInstance نمونه ایجاد شده را برمی‌گرداند، و اگر نمونه‌ای وجود نداشته باشد، یک نمونه جدید ایجاد می‌کند.به عنوان مثال فرض کنید میخواید یک کلاس Logger داشته باشید برای ثبت لاگ های سیستمتون ، میاید از این الگو استفاده میکنید.Factory Method Pattern:الگوی Factory Method یکی از الگوهای طراحی نرم‌افزار است که در دنیای برنامه‌نویسی مورد استفاده قرار می‌گیرد. این الگو به شما این امکان را می‌دهد که یک روش (معمولاً به نام Factory Method) در یک کلاس تعریف کنید که مسئول ایجاد نمونه‌های یک کلاس دیگر (یا زیرکلاس‌های آن) باشد. به این ترتیب، شما از ایجاد مستقیم نمونه‌ها در کد خود جلوگیری می‌کنید و تغییرات در ایجاد نمونه‌ها را به سادگی اعمال می‌کنید.در زیر، یک مثال ساده از Factory Method Pattern در C# ارائه می‌شود:using System; // این کلاس اصلی است که نمونه‌های آن را ایجاد می‌کنیم. public abstract class Product { public abstract void ShowInfo(); } // کلاس‌های زیرکلاس برای ایجاد نمونه‌های مختلف محصول public class ConcreteProductA : Product { public override void ShowInfo() { Console.WriteLine(&quotProduct A is created.&quot); }} public class ConcreteProductB : Product { public override void ShowInfo() { Console.WriteLine(&quotProduct B is created.&quot); } } // Factory Method که توسط کلاس‌های زیرکلاس پیاده‌سازی می‌شود public abstract class Creator { public abstract Product FactoryMethod(); } public class ConcreteCreatorA : Creator { public override Product FactoryMethod() { return new ConcreteProductA(); } } public class ConcreteCreatorB : Creator { public override Product FactoryMethod() { return new ConcreteProductB(); } } class Program { static void Main(string[] args) { // استفاده از Factory Method برای ایجاد نمونه‌های محصول Creator creatorA = new ConcreteCreatorA(); Product productA = creatorA.FactoryMethod(); productA.ShowInfo(); Creator creatorB = new ConcreteCreatorB(); Product productB = creatorB.FactoryMethod(); productB.ShowInfo(); } }در این مثال، الگوی Factory Method برای ایجاد نمونه‌های دو محصول مختلف (Product A و Product B) استفاده شده است. Creator و ConcreteCreator کلاس‌ها مسئول ایجاد نمونه‌ها هستند و با فراخوانی Factory Method، نمونه‌های مختلف Product ایجاد می‌شوند.یک مثال واقعی از استفاده از Factory Method Pattern می‌تواند در فریمورک‌های متعددی که از این الگو استفاده می‌کنند، مانند ASP.NET یا Entity Framework، مشاهده شود. به عنوان مثال، در ASP.NET، وقتی که یک درخواست HTTP وارد می‌شود، یک Factory Method برای ایجاد نمونه‌های کنترلر‌ها (Controller) استفاده می‌شود تا منطق مربوط به پردازش درخواست را ایجاد و مدیریت کند.Observer Pattern:لگوی Observer یکی از الگوهای طراحی مهم در برنامه‌نویسی است که به شما امکان مشاهده تغییرات در یک شیء را می‌دهد و سایر شیء‌ها را از تغییرات مطلع می‌کند. این الگو به شما این امکان را می‌دهد که اشیاء مختلفی را به یک شیء مشاهده‌کننده (Observer) متصل کنید تا هنگامی که تغییرات در شیء مشاهده‌شونده (Subject) رخ دهد، اطلاع رسانی شوند.در زبان C#، می‌توانیم الگوی Observer را با استفاده از ویژگی‌ها و رویدادها (Events) پیاده‌سازی کنیم. در ادامه یک مثال ساده از الگوی Observer در C# ارائه می‌دهم:using System; using System.Collections.Generic; // مشاهده‌کننده public interface IObserver { void Update(string message); } // مشاهده‌شونده public class Subject { private List<IObserver> observers = new List<IObserver>(); private string message; public void Attach(IObserver observer) { observers.Add(observer); } public void Detach(IObserver observer) { observers.Remove(observer); } public void SetMessage(string message) { this.message = message; NotifyObservers(); } private void NotifyObservers() { foreach (var observer in observers) { observer.Update(message); } } } // مشاهده‌کننده اول public class ConcreteObserver1 : IObserver { public void Update(string message) { Console.WriteLine(&quotمشاهده‌کننده 1: &quot + message); } } // مشاهده‌کننده دوم public class ConcreteObserver2 : IObserver { public void Update(string message) { Console.WriteLine(&quotمشاهده‌کننده 2: &quot + message); } } class Program { static void Main() { // ایجاد یک شیء مشاهده‌شونده var subject = new Subject(); // ایجاد دو مشاهده‌کننده var observer1 = new ConcreteObserver1(); var observer2 = new ConcreteObserver2(); // اتصال مشاهده‌کننده‌ها به شیء مشاهده‌شونده subject.Attach(observer1); subject.Attach(observer2); // تنظیم پیام و اعلام به مشاهده‌کننده‌ها subject.SetMessage(&quotپیام جدید!&quot); // حذف یکی از مشاهده‌کننده‌ها subject.Detach(observer1); // تنظیم پیام دیگر و اعلام به مشاهده‌کننده‌ها (بدون مشاهده‌کننده 1) subject.SetMessage(&quotپیام دیگر!&quot); } }در این مثال، ما یک شیء مشاهده‌شونده (Subject) و دو مشاهده‌کننده (ConcreteObserver1 و ConcreteObserver2) ایجاد کرده‌ایم. مشاهده‌کننده‌ها به شیء مشاهده‌شونده متصل می‌شوند و هنگامی که پیامی تنظیم می‌شود، تغییرات به تمام مشاهده‌کننده‌ها اعلام می‌شود. همچنین، می‌توانیم مشاهده‌کننده‌ها را از شیء مشاهده‌شونده حذف کنیم. در نهایت، اجرای این برنامه نمونه نحوه کارکرد الگوی Observer را نشان می‌دهد.Strategy Pattern:الگوی استراتژی (Strategy Pattern) یکی از الگوهای طراحی مهم در برنامه‌نویسی است که به شما امکان انتخاب یکی از چند روش (استراتژی) برای انجام یک عملیات خاص را می‌دهد و این انتخاب را در زمان اجرا انجام می‌دهد. این الگو به شما این امکان را می‌دهد که رفتارهای مختلف را در یک کلاس اصلی تعریف کرده و از آنها به عنوان یک استراتژی جداگانه استفاده کنید.در زبان C#، می‌توانیم الگوی استراتژی را با استفاده از اشیاء و اینترفیس‌ها پیاده‌سازی کنیم. در ادامه، یک مثال از الگوی استراتژی در C# ارائه می‌دهم:using System; // اینترفیس استراتژی public interface IStrategy { void Execute(); } // استراتژی اول public class ConcreteStrategy1 : IStrategy { public void Execute() { Console.WriteLine(&quotاستراتژی اول را اجرا می‌کند.&quot); } } // استراتژی دوم public class ConcreteStrategy2 : IStrategy { public void Execute() { Console.WriteLine(&quotاستراتژی دوم را اجرا می‌کند.&quot); } } // کلاس محیط public class Context { private IStrategy strategy; public Context(IStrategy strategy) { this.strategy = strategy; } public void ExecuteStrategy() { strategy.Execute(); } } class Program { static void Main() { // ایجاد یک محیط با استفاده از استراتژی اول var context1 = new Context(new ConcreteStrategy1()); context1.ExecuteStrategy(); // ایجاد یک محیط با استفاده از استراتژی دوم var context2 = new Context(new ConcreteStrategy2()); context2.ExecuteStrategy(); } }در این مثال، ما دو استراتژی (ConcreteStrategy1 و ConcreteStrategy2) ایجاد کرده‌ایم، هرکدام از این استراتژی‌ها یک روش Execute را پیاده‌سازی می‌کنند. سپس یک کلاس محیط (Context) ایجاد می‌کنیم که یک شیء از یک استراتژی را دریافت می‌کند و این استراتژی را در زمان اجرا اجرا می‌کند.در داخل تابع Main، ما دو محیط ایجاد می‌کنیم و به هر یک از آنها یک استراتژی مخصوص می‌دهیم و سپس این محیط‌ها را فرا می‌خوانیم. به این ترتیب، ما می‌توانیم رفتارهای مختلف را در زمان اجرا انتخاب کنیم (استراتژی مورد استفاده) و به اجرای آنها بپردازیم. این نحوه عملکرد الگوی استراتژی را نمایش می‌دهد.Decorator Pattern:الگوی Decorator یکی از الگوهای طراحی ساختاری است که به شما امکان افزودن عملکرد به یک شیء بدون تغییر کد آن شیء را می‌دهد. این الگو از ارث‌بری (Inheritance) جلوگیری کرده و به شما امکان انعطاف‌پذیری بیشتر در افزودن ویژگی‌ها و عملکردها به یک شیء را می‌دهد.در زبان C#، الگوی Decorator را معمولاً با استفاده از ارث‌بری از یک رابط یا کلاس پایه (Base Class) پیاده‌سازی می‌کنیم. سپس کلاس‌های مختلفی را برای افزودن ویژگی‌های جدید به شیء اصلی ایجاد می‌کنیم. در ادامه یک مثال از الگوی Decorator در C# ارائه می‌دهم:using System; // رابط کلاس پایه (Component) public interface ICoffee { string GetDescription(); double GetCost(); } // کلاس پایه (Concrete Component) public class SimpleCoffee : ICoffee { public string GetDescription() { return &quotقهوه ساده&quot } public double GetCost() { return 2.0; } } // کلاس Decorator انتزاعی (Decorator) public abstract class CoffeeDecorator : ICoffee { protected ICoffee _coffee; public CoffeeDecorator(ICoffee coffee) { _coffee = coffee; } public virtual string GetDescription() { return _coffee.GetDescription(); } public virtual double GetCost() { return _coffee.GetCost(); } } // کلاس‌های Decorator ویژگی‌ها را اضافه می‌کنند public class MilkDecorator : CoffeeDecorator { public MilkDecorator(ICoffee coffee) : base(coffee) { } public override string GetDescription() { return base.GetDescription() + &quot, با شیر&quot } public override double GetCost() { return base.GetCost() + 1.0; } } public class SugarDecorator : CoffeeDecorator { public SugarDecorator(ICoffee coffee) : base(coffee) { } public override string GetDescription() { return base.GetDescription() + &quot, با قند&quot } public override double GetCost() { return base.GetCost() + 0.5; } } class Program { static void Main() { // ایجاد یک قهوه ساده ICoffee coffee = new SimpleCoffee(); Console.WriteLine(&quotتوضیحات: &quot + coffee.GetDescription()); Console.WriteLine(&quotقیمت: &quot + coffee.GetCost()); // افزودن شیر به قهوه coffee = new MilkDecorator(coffee); Console.WriteLine(&quotتوضیحات: &quot + coffee.GetDescription()); Console.WriteLine(&quotقیمت: &quot + coffee.GetCost()); // افزودن قند به قهوه coffee = new SugarDecorator(coffee); Console.WriteLine(&quotتوضیحات: &quot + coffee.GetDescription()); Console.WriteLine(&quotقیمت: &quot + coffee.GetCost()); } }در این مثال، ما یک کلاس پایه به نام SimpleCoffee داریم که ICoffee را پیاده‌سازی می‌کند. سپس دو کلاس Decorator به نام‌های MilkDecorator و SugarDecorator را ایجاد می‌کنیم که از CoffeeDecorator ارث‌بری می‌کنند و ویژگی‌های اضافی به قهوه اضافه می‌کنند.در تابع Main، ابتدا یک قهوه ساده ایجاد می‌کنیم و سپس با استفاده از Decoratorها، شیر و قند به قهوه اضافه می‌کنیم. از طریق Decoratorها، می‌توانیم به شیء اصلی (قهوه) ویژگی‌های جدید (شیر و قند) اضافه کنیم بدون ایجاد تغییر در کد اصلی قهوه. این نحوه عملکرد الگوی Decorator را نمایش می‌دهد.امید وارم مفید بوده باشه ....