Паттерны проектирования в C#: полное руководство с примерами
Паттерны проектирования (Design Patterns) — это проверенные решения типовых задач, возникающих при разработке программного обеспечения. В экосистеме .NET и C# знание паттернов особенно важно, так как многие из них встроены непосредственно в платформу (например, IDisposable для паттерна Dispose). В этой статье мы разберём три основные группы паттернов по классификации GoF (Gang of Four) с практическими примерами на C#.
Порождающие паттерны (Creational Patterns)
Порождающие паттерны отвечают за создание объектов, делая систему независимой от способа их создания, композиции и представления.
Singleton (Одиночка)
Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В C# часто используется для работы с логгерами, кэшем или конфигурацией.
public sealed class Logger
{
private static readonly Lazy<Logger> _instance =
new Lazy<Logger>(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
public void Log(string message)
{
Console.WriteLine($"[{DateTime.Now}] {message}");
}
}
// Использование
Logger.Instance.Log("Приложение запущено");
Factory Method (Фабричный метод)
Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Отлично подходит для логики, которая должна быть расширяемой.
public interface ITransport
{
void Deliver();
}
public class Truck : ITransport
{
public void Deliver() => Console.WriteLine("Доставка по дороге");
}
public class Ship : ITransport
{
public void Deliver() => Console.WriteLine("Доставка по морю");
}
public abstract class Logistics
{
public abstract ITransport CreateTransport();
public void PlanDelivery()
{
var transport = CreateTransport();
transport.Deliver();
}
}
public class RoadLogistics : Logistics
{
public override ITransport CreateTransport() => new Truck();
}
public class SeaLogistics : Logistics
{
public override ITransport CreateTransport() => new Ship();
}
// Использование
Logistics logistics = new RoadLogistics();
logistics.PlanDelivery(); // Доставка по дороге
Структурные паттерны (Structural Patterns)
Структурные паттерны определяют способы построения классов и объектов для формирования более крупных структур.
Adapter (Адаптер)
Преобразует интерфейс одного класса в другой интерфейс, ожидаемый клиентом. Часто применяется для интеграции со сторонними библиотеками или устаревшим кодом.
// Целевой интерфейс (ITarget)
public interface IXmlParser
{
string ParseToXml(string data);
}
// Адаптируемый класс (Adaptee)
public class JsonParser
{
public string ParseToJson(string data)
{
return $"{{\"data\": \"{data}\"}}";
}
}
// Адаптер
public class JsonToXmlAdapter : IXmlParser
{
private readonly JsonParser _jsonParser;
public JsonToXmlAdapter(JsonParser jsonParser)
{
_jsonParser = jsonParser;
}
public string ParseToXml(string data)
{
var json = _jsonParser.ParseToJson(data);
// Простейшая конвертация JSON в XML
return $"<data>{data}</data>";
}
}
// Использование
var adapter = new JsonToXmlAdapter(new JsonParser());
string xml = adapter.ParseToXml("Hello");
Console.WriteLine(xml); // <data>Hello</data>
Decorator (Декоратор)
Динамически добавляет объекту новые обязанности. В C# этот паттерн часто реализуется через наследование или композицию.
public interface INotifier
{
void Send(string message);
}
public class EmailNotifier : INotifier
{
public void Send(string message)
{
Console.WriteLine($"Email отправлен: {message}");
}
}
public abstract class NotifierDecorator : INotifier
{
protected INotifier _notifier;
public NotifierDecorator(INotifier notifier)
{
_notifier = notifier;
}
public virtual void Send(string message)
{
_notifier.Send(message);
}
}
public class SmsDecorator : NotifierDecorator
{
public SmsDecorator(INotifier notifier) : base(notifier) { }
public override void Send(string message)
{
base.Send(message);
Console.WriteLine($"SMS отправлено: {message}");
}
}
// Использование
INotifier notifier = new EmailNotifier();
notifier = new SmsDecorator(notifier);
notifier.Send("Важное уведомление");
// Вывод:
// Email отправлен: Важное уведомление
// SMS отправлено: Важное уведомление
Поведенческие паттерны (Behavioral Patterns)
Поведенческие паттерны определяют алгоритмы и распределение обязанностей между объектами.
Observer (Наблюдатель)
Создаёт механизм подписки, позволяющий одним объектам следить за событиями, происходящими в других объектах. В C# для этого используются события и делегаты.
public class Stock
{
private decimal _price;
public event EventHandler<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
if (_price != value)
{
_price = value;
PriceChanged?.Invoke(this, _price);
}
}
}
}
public class Investor
{
public string Name { get; set; }
public void OnPriceChanged(object sender, decimal newPrice)
{
Console.WriteLine($"{Name} получил уведомление: новая цена {newPrice:C}");
}
}
// Использование
var stock = new Stock();
var investor1 = new Investor { Name = "Иван" };
var investor2 = new Investor { Name = "Мария" };
stock.PriceChanged += investor1.OnPriceChanged;
stock.PriceChanged += investor2.OnPriceChanged;
stock.Price = 150.50m;
// Вывод:
// Иван получил уведомление: новая цена 150,50 ₽
// Мария получил уведомление: новая цена 150,50 ₽
Strategy (Стратегия)
Определяет семейство взаимозаменяемых алгоритмов и инкапсулирует каждый из них. Позволяет изменять поведение объекта во время выполнения.
public interface ISortStrategy
{
void Sort(List<int> list);
}
public class BubbleSort : ISortStrategy
{
public void Sort(List<int> list)
{
Console.WriteLine("Сортировка пузырьком...");
list.Sort(); // упрощённая реализация
}
}
public class QuickSort : ISortStrategy
{
public void Sort(List<int> list)
{
Console.WriteLine("Быстрая сортировка...");
list.Sort();
}
}
public class SortedList
{
private