5 دیزاین پترن رایج

دیزاین پترن‌ها در واقع راه حل‌هایی برای پیاده‌سازی نرم‌افزار هستند که سبب سهولت و امکان استفاده مجدد از کدهای نوشته شده می‌شوند. در ذیل 5 نمونه رایج از این الگوها آورده شده:

1- Singleton
این الگو برای همه قابل درک است، در اکثر پروژه‌ها استفاده می‌شود و بخش لازم در پیاده‌سازی سایر الگوهای طراحی هستند.
این الگو ضمانت می‌کند که تنها یک نسخه از کلاس همیشه وجود داشته باشد. مثال:

class Singleton
{
    // Create a new instance if none exist already
    public static Singleton Instance { get; } = new Singleton();

    private Singleton()
    {
    
    }
}

2- Facade
دراین نوع طراحی شما تنها بخش جلویی کلاس را می‌بینید و فعالیت‌های داخل کلاس بی‌خبر خواهید بود. در این حالت فعالیت‌های پیچیده از دید کاربر مخفی خواهد بود و کلاس مربوطه این وظایف را انجام می‌دهد.
فرض کنید که منزلی شامل بخش‌های خانه و کارهای لازم در خانه است و شامل لازم دارید این موارد را جهت برپایی مهمانی آماده کنید:

public interface IHousing
{
    public void CleanRooms(Room room);
    public void CookMeal(Meal meal);
    public void TurnOnHeating(Room room, float level);
    public void TurnOffHeating(Room room);
    public void Garden();
}

public interface ILiving
{
    public void Shopping(object[] items);
    public int InviteGuests(int guests);
    public void ServeBeverage(Beverage beverage);
    public void PayInvoices();
}

حال با استفاده از الگوی Facade به راحتی می‌توان این فرآیند پیچیده را حل کرد:

public interface IPartyFacade
{
    public void StartParty();
    public void StopParty();
}

public class PartyFacade : IPartyFacade
{
    private readonly IHousing _housing = new Housing();
    private readonly ILiving _living = new Living();
   
    public void StartParty()
    {
        _housing.CleanRoom(Room.PartyRoom);
        _housing.Garden();
        _living.Shopping(...);
        _housing.CookMeal(Meal.Pizza);
        _living.InviteGuests(5);
        _living.ServeBeverage(Beverage.Beer);
        _living.ServeBeverage(Beverage.Wine);
        _housing.TurnOnHeating(Room.PartyRoom);
    }
    
    public void StopParty()
    {
        _living.PayInvoices();
        _housing.CleanRoom(Room.Kitchen);
        _housing.CleanRoom(Room.PartyRoom);
        _housing.TurnOffHeating(Room.PartyRoom);
    }
}

در این حالت شما تنها مهمانی را شروع کرده و به پایان می‌برید، موارد ریزتر توسط خود کلاس هندل می‌شود.

3- Adapter
این الگو مانند پلی برای پیاده‌سازی دو اینترفیس است که شامل کلاسی است که مسئول برقراری ارتباط بین دو اینترفیس مجزا می‌باشد. مانند وسیله‌ای که تاکنون تنها می‌توانست گوشی‌های اپل را شارژ کند اما حالا نیاز دارید که گوشی‌های اندرویدی را نیز شارژ کنید. در این حالت کلاس PhoneAdapter واسطی برای پیاده‌سازی اینترقیس‌های اپل و اندروید می‌شود.

namespace DoFactory.GangOfFour.Adapter.Structural
{
  /// <summary>

  /// MainApp startup class for Structural

  /// Adapter Design Pattern.

  /// </summary>

  class MainApp

  {
    /// <summary>

    /// Entry point into console application.

    /// </summary>

    static void Main()
    {
      // Create adapter and place a request

      Target target = new Adapter();
      target.Request();
 
      // Wait for user

      Console.ReadKey();
    }
  }
 
  /// <summary>

  /// The 'Target' class

  /// </summary>

  class Target

  {
    public virtual void Request()
    {
      Console.WriteLine("Called Target Request()");
    }
  }
 
  /// <summary>

  /// The 'Adapter' class

  /// </summary>

  class Adapter : Target

  {
    private Adaptee _adaptee = new Adaptee();
 
    public override void Request()
    {
      // Possibly do some other work

      //  and then call SpecificRequest

      _adaptee.SpecificRequest();
    }
  }
 
  /// <summary>

  /// The 'Adaptee' class

  /// </summary>

  class Adaptee

  {
    public void SpecificRequest()
    {
      Console.WriteLine("Called SpecificRequest()");
    }
  }
}

https://www.dofactory.com/net/adapter-design-pattern
https://refactoring.guru/design-patterns/adapter/csharp/example

4- Decorator
این نوع الگوی طراحی سبب گسترش base class با عملکردهای جدیدی که در base class وجود ندارد، بدون تاثیرگذاری روی پیاده‎سازی base class می‌شود. به عبارتی دیگر افزودن عملکردی جدید به یک کلاس بدون تغییر ساختار کلاس است. به نحوی بیان کننده کلاس‌های از نوع abstract هستند.

    public interface IHouse
    {
        string GetDetails();
        double GetPrice();
    }

    public class AtticHouse : IHouse
    {
        public double GetPrice()
        {
            return 30000;
        }

        public string GetDetails()
        {
            return "House with attic";
        }
    }

    public class FlatRoofHouse : IHouse
    {
        public double GetPrice()
        {
            return 20000;
        }

        public string GetDetails()
        {
            return "House with flat roof";
        }
    }


    public abstract class HouseAccessories : IHouse
    {
        private readonly IHouse _house;

        public HouseAccessories(IHouse house)
        {
            _house = house;
        }

        public virtual double GetPrice()
        {
            return _house.GetPrice();
        }

        public virtual string GetDetails()
        {
            return _house.GetDetails();
        }
    }

    public class MultiStoryPackage : HouseAccessories
    {
        public MultiStoryPackage(IHouse house) : base(house)
        {

        }

        public override string GetDetails()
        {
            return base.GetDetails() + " + Multi Story Package";
        }

        public override double GetPrice()
        {
            return base.GetPrice() + 5000;
        }

    }

    public class CustomPaintingPackage : HouseAccessories
    {
        public CustomPaintingPackage(IHouse house) : base(house)
        {

        }

        public override string GetDetails()
        {
            return base.GetDetails() + " + Custom Painting Package";
        }

        public override double GetPrice()
        {
            return base.GetPrice() + 1000;
        }
    }

    public class RealEstateAgent
    {
        public static void SellHouse()
        {
            var basicHouse = new AtticHouse();
            HouseAccessories upgraded = new MultiStoryPackage(basicHouse);
            upgraded = new CustomPaintingPackage(upgraded);

            Console.WriteLine($"House: '{upgraded.GetDetails()}' Cost: {upgraded.GetPrice()}");
        }
    }

5- Dependency Injection
یکی از محبوب‌ترین و نام آشناترین طراحی‌ها است. ایده اصلی این نوع طراحی، ایجاد separation of concerns براساس قراردادن تولید آبجکت، جدا و مجزا از کاربرد آبجکت می‌باشد که سبب خوانایی بیشتر کد و استفاده مجدد از کدها می‌شود اما سبب پیچیدگی می‌شود.

public class ExampleClass
{
    // the dependend class to be injected. Must be injected with the constructor.
    private readonly IMyDependency _myDependency;

    // While constructing an instance of our class, we inject an instance of the dependent class.
    public ExampleClass(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }
    
    public void OnGet()
    {
        _myDependency.Write("Successfully injected our dependency.");
    }
}

همانطور که در کد می‌بینید، dependency توسط اینترفیس پیاده‌سازی شده است.کلاس پیاده کننده اینترفیس نیازی به دانستن نوع کلاس اینجکت شده ندارد و تنها چیزی که لازم است از آن مطلع باشد این است که dependency دارای یک متد به نام Write است.
dependency می‌تواند به یکی از سه طریق به کلاس تزریق شود:
1- Constructor Injection
2-Property Injection
3-Method Injection

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
  
namespace propertyinjuction  
{  
    public interface text  
    {
        void print();
    }
    class format : text
    {
        public void print()
        {
            Console.WriteLine(" here is text format");
        }      
    }
    // constructor injection
    public class constructorinjection
    {  
        private text _text;
        public constructorinjection(text t1)
        {
            this._text = t1;          
        }
        public void print()
        {  
            _text.print();
        }
    }
    class constructor
    {  
        static void Main(string[] args)
        {  
            constructorinjection cs = new constructorinjection(new format());
            cs.print();
            Console.ReadKey();          
        }
    }
}

این الگو شامل 3 نوع کلاس می‌شود:
1- Client Class: کلاس وابسته به یک آبجکت دیگر است (در اینجا کلاس سرویس).
2- Service Class: این کلاس، سرویس‌های لازم برای کلاس کلاینت را فراهم می‌کند (وابستگی کلاس کلاینت است).
3- Injector Class: این کلاس، کلاس سرویس را داخل کلاس کلاینت پیاده‌سازی می‌کند.

https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
https://www.c-sharpcorner.com/UploadFile/85ed7a/dependency-injection-in-C-Sharp/
https://dotnettutorials.net/lesson/dependency-injection-design-pattern-csharp/