Какие GoF-паттерны использовали?csharp-7

Паттерны GoF — это 23 классических паттерна проектирования, описанных в книге "Design Patterns: Elements of Reusable Object-Oriented Software". Они делятся на три категории: порождающие, структурные и поведенческие. Вот наиболее часто используемые из них в C# разработке:

1. Порождающие паттерны

Singleton

Гарантирует, что у класса есть только один экземпляр.

public sealed class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            lock (_lock)
            {
                return _instance ??= new Singleton();
            }
        }
    }
}

Factory Method

Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.

public interface IProduct {}
public class ConcreteProduct : IProduct {}

public abstract class Creator
{
    public abstract IProduct FactoryMethod();
}

public class ConcreteCreator : Creator
{
    public override IProduct FactoryMethod() => new ConcreteProduct();
}

2. Структурные паттерны

Adapter

Позволяет объектам с несовместимыми интерфейсами работать вместе.

public interface ITarget { void Request(); }

public class Adaptee { public void SpecificRequest() {} }

public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

    public Adapter(Adaptee adaptee) => _adaptee = adaptee;
    public void Request() => _adaptee.SpecificRequest();
}

Decorator

Динамически добавляет объекту новые обязанности.

public interface IComponent { void Operation(); }

public class ConcreteComponent : IComponent
{
    public void Operation() => Console.Write("ConcreteComponent");
}

public class Decorator : IComponent
{
    private readonly IComponent _component;

    public Decorator(IComponent component) => _component = component;
    public virtual void Operation() => _component.Operation();
}

public class ConcreteDecorator : Decorator
{
    public ConcreteDecorator(IComponent component) : base(component) {}
    public override void Operation()
    {
        base.Operation();
        Console.Write(" + Decorator");
    }
}

3. Поведенческие паттерны

Observer

Определяет зависимость "один-ко-многим" между объектами.

public interface IObserver { void Update(); }

public interface ISubject
{
    void Attach(IObserver observer);
    void Detach(IObserver observer);
    void Notify();
}

public class ConcreteSubject : ISubject
{
    private readonly List<IObserver> _observers = new();
    public void Attach(IObserver observer) => _observers.Add(observer);
    public void Detach(IObserver observer) => _observers.Remove(observer);
    public void Notify() => _observers.ForEach(o => o.Update());
}

Strategy

Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.

public interface ISortStrategy
{
    void Sort(List<int> list);
}

public class QuickSort : ISortStrategy
{
    public void Sort(List<int> list) => Console.WriteLine("QuickSort");
}

public class Context
{
    private ISortStrategy _strategy;
    public void SetStrategy(ISortStrategy strategy) => _strategy = strategy;
    public void ExecuteSort(List<int> list) => _strategy.Sort(list);
}

Практическое применение в .NET

  1. Singleton - HttpContext.Current в ASP.NET
  2. Observer - События (events) в C#
  3. Strategy - Различные алгоритмы сортировки в List<T>.Sort(IComparer<T>)
  4. Decorator - Stream и его декораторы (BufferedStream, CryptoStream)

Антипаттерны использования

  1. Злоупотребление Singleton - может привести к трудностям тестирования
  2. Глубокие иерархии Decorator - усложняют понимание кода
  3. Избыточные Factory Methods - когда можно использовать простые конструкторы

Резюмируем:

паттерны GoF предоставляют проверенные решения распространенных проблем проектирования ПО. В C# они часто реализуются с помощью интерфейсов, абстрактных классов и делегатов. Правильное применение паттернов делает код более гибким, расширяемым и поддерживаемым, но важно избегать их избыточного использования там, где можно обойтись более простыми решениями.