Паттерны проектирования в C#: полное руководство с примерами кода

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в C#: полное руководство с примерами

Паттерны проектирования (design patterns) — это проверенные решения типовых задач при разработке программного обеспечения. В экосистеме C# и .NET они помогают создавать гибкую, масштабируемую и поддерживаемую архитектуру. В этой статье мы разберем четыре ключевых паттерна: Singleton, Factory Method, Observer и Strategy, с реальными примерами на C#.

Зачем нужны паттерны проектирования в C#?

Паттерны не являются готовыми библиотеками или фреймворками. Это концептуальные шаблоны, которые:

  • Упрощают коммуникацию — разработчики используют единую терминологию
  • Повышают качество кода — уменьшают связанность и увеличивают гибкость
  • Экономят время — не нужно изобретать велосипед
  • Соответствуют принципам SOLID — большинство паттернов реализуют эти принципы

1. Singleton (Одиночка)

Назначение: Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему.

Когда использовать:

  • Логгеры
  • Менеджеры конфигураций
  • Пул соединений с базой данных
  • Кэши

Пример на C# (потокобезопасная реализация):

public sealed class ConfigurationManager
{
    private static readonly Lazy<ConfigurationManager> _instance = 
        new Lazy<ConfigurationManager>(() => new ConfigurationManager());

    private readonly Dictionary<string, string> _settings;

    private ConfigurationManager()
    {
        _settings = new Dictionary<string, string>
        {
            { "ConnectionString", "Server=localhost;Database=myDb" },
            { "ApiKey", "12345-ABCDE" }
        };
    }

    public static ConfigurationManager Instance => _instance.Value;

    public string GetSetting(string key)
    {
        return _settings.TryGetValue(key, out var value) ? value : null;
    }
}

// Использование
var config = ConfigurationManager.Instance;
Console.WriteLine(config.GetSetting("ConnectionString"));

Плюсы:

  • Контролируемый доступ к единственному экземпляру
  • Ленивая инициализация (Lazy<T>)
  • Потокобезопасность без блокировок

2. Factory Method (Фабричный метод)

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

Когда использовать:

  • Когда заранее неизвестен конкретный тип объекта
  • Когда создание объекта требует сложной логики
  • Для разделения кода создания и использования

Пример на C#:

// Абстрактный продукт
public interface ILogger
{
    void Log(string message);
}

// Конкретные продукты
public class FileLogger : ILogger
{
    public void Log(string message) => 
        File.AppendAllText("log.txt", $"{DateTime.Now}: {message}\n");
}

public class ConsoleLogger : ILogger
{
    public void Log(string message) => 
        Console.WriteLine($"{DateTime.Now}: {message}");
}

// Создатель с фабричным методом
public abstract class LoggerFactory
{
    public abstract ILogger CreateLogger();
    
    public void LogMessage(string message)
    {
        var logger = CreateLogger();
        logger.Log(message);
    }
}

public class FileLoggerFactory : LoggerFactory
{
    public override ILogger CreateLogger() => new FileLogger();
}

public class ConsoleLoggerFactory : LoggerFactory
{
    public override ILogger CreateLogger() => new ConsoleLogger();
}

// Использование
LoggerFactory factory = new ConsoleLoggerFactory();
factory.LogMessage("Привет, мир!");

3. Observer (Наблюдатель)

Назначение: Определяет зависимость "один ко многим" между объектами так, что при изменении состояния одного объекта все зависимые оповещаются и обновляются автоматически.

Когда использовать:

  • Системы событий и уведомлений
  • UI-компоненты (MVVM, WinForms)
  • Мониторинг изменений данных

Пример на C# с использованием событий:

public class StockMarket
{
    private decimal _price;
    public event EventHandler<decimal> PriceChanged;

    public decimal Price
    {
        get => _price;
        set
        {
            if (_price != value)
            {
                _price = value;
                OnPriceChanged(_price);
            }
        }
    }

    protected virtual void OnPriceChanged(decimal newPrice)
    {
        PriceChanged?.Invoke(this, newPrice);
    }
}

public class Investor
{
    public string Name { get; }
    
    public Investor(string name, StockMarket market)
    {
        Name = name;
        market.PriceChanged += OnPriceChanged;
    }

    private void OnPriceChanged(object sender, decimal newPrice)
    {
        Console.WriteLine($"{Name} получил уведомление: новая цена {newPrice:C}");
    }
}

// Использование
var market = new StockMarket();
var investor1 = new Investor("Иван", market);
var investor2 = new Investor("Мария", market);

market.Price = 150.50m;
market.Price = 152.30m;

4. Strategy (Стратегия)

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

Когда использовать:

  • Разные способы сортировки, фильтрации или валидации
  • Расчет стоимости с разными скидками
  • Алгоритмы сжатия или шифрования

Пример на C#:

// Интерфейс стратегии
public interface ISortStrategy
{
    void Sort(int[] array);
}

// Конкретные стратегии
public class BubbleSortStrategy : ISortStrategy
{
    public void Sort(int[] array)
    {
        for (int i = 0; i < array.Length - 1; i++)
            for (int j = 0; j < array.Length - i - 1; j++)
                if (array[j] > array[j + 1])
                    (array[j], array[j + 1]) = (array[j + 1], array[j]);
        
        Console.WriteLine("Отсортировано пузырьком");
    }
}

public class QuickSortStrategy : ISortStrategy
{
    public void Sort(int[] array)
    {
        QuickSort(array, 0, array.Length - 1);
        Console.WriteLine("Отсортировано быстрой сортировкой");                

Блоги

Рекомендаций по книгам