Какие знаешь структурные паттерны?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Структурные паттерны проектирования в C#
Структурные паттерны проектирования — это шаблоны, которые помогают организовать классы и объекты в более сложные и гибкие структуры, сохраняя при этом эффективность и минимальную связанность кода. В контексте C# и backend-разработки они особенно полезны для построения масштабируемых, поддерживаемых архитектур. Вот ключевые структурные паттерны, которые я активно использую:
1. Адаптер (Adapter)
Паттерн позволяет объектам с несовместимыми интерфейсами работать вместе. В backend-разработке часто применяется для интеграции сторонних библиотек или устаревших систем.
// Пример: адаптер для старой системы логирования
public interface ILogger
{
void Log(string message);
}
public class LegacyLogger
{
public void WriteLog(string text)
{
Console.WriteLine($"Legacy: {text}");
}
}
public class LoggerAdapter : ILogger
{
private readonly LegacyLogger _legacyLogger;
public LoggerAdapter(LegacyLogger legacyLogger)
{
_legacyLogger = legacyLogger;
}
public void Log(string message)
{
_legacyLogger.WriteLog(message);
}
}
// Использование
var legacyLogger = new LegacyLogger();
ILogger logger = new LoggerAdapter(legacyLogger);
logger.Log("Адаптированное сообщение");
2. Мост (Bridge)
Разделяет абстракцию и реализацию, позволяя им изменяться независимо. Полезен при работе с разными провайдерами (например, базы данных, платежные системы).
public interface IDataStorage // Реализация
{
void Save(string data);
}
public abstract class DataProcessor // Абстракция
{
protected IDataStorage _storage;
protected DataProcessor(IDataStorage storage)
{
_storage = storage;
}
public abstract void ProcessAndSave(string data);
}
public class JsonDataProcessor : DataProcessor
{
public JsonDataProcessor(IDataStorage storage) : base(storage) {}
public override void ProcessAndSave(string data)
{
var json = $"{{ \"data\": \"{data}\" }}";
_storage.Save(json);
}
}
3. Компоновщик (Composite)
Позволяет сгруппировать объекты в древовидные структуры и работать с ними как с единым объектом. Часто используется для построения UI-компонентов или файловых систем.
public abstract class FileSystemComponent
{
public string Name { get; }
public abstract long GetSize();
}
public class File : FileSystemComponent
{
private long _size;
public override long GetSize() => _size;
}
public class Directory : FileSystemComponent
{
private List<FileSystemComponent> _children = new List<FileSystemComponent>();
public void Add(FileSystemComponent component)
{
_children.Add(component);
}
public override long GetSize()
{
return _children.Sum(c => c.GetSize());
}
}
4. Декоратор (Decorator)
Динамически добавляет объекту новые обязанности. В C# часто реализуется через наследование или с использованием встроенных возможностей (например, System.IO.Stream).
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;
protected NotifierDecorator(INotifier notifier)
{
_notifier = notifier;
}
public virtual void Send(string message)
{
_notifier.Send(message);
}
}
public class SmsNotifierDecorator : NotifierDecorator
{
public SmsNotifierDecorator(INotifier notifier) : base(notifier) {}
public override void Send(string message)
{
base.Send(message);
Console.WriteLine($"SMS: {message}");
}
}
5. Фасад (Facade)
Представляет простой интерфейс к сложной подсистеме. В backend-разработке это типичный пример для упрощения работы с внешними API или сложными библиотеками.
public class OrderProcessingFacade
{
private InventoryService _inventory;
private PaymentService _payment;
private ShippingService _shipping;
public OrderProcessingFacade()
{
_inventory = new InventoryService();
_payment = new PaymentService();
_shipping = new ShippingService();
}
public void ProcessOrder(Order order)
{
_inventory.CheckStock(order);
_payment.ProcessPayment(order);
_shipping.ScheduleDelivery(order);
}
}
6. Приспособленец (Flyweight)
Экономит память, разделяя общее состояние между множеством объектов. Полезен для работы с большими объемами данных (например, кэширование строк или графических объектов).
public class CharacterFlyweight
{
public char Symbol { get; }
public string FontFamily { get; }
// Другие общие данные
}
public class FlyweightFactory
{
private Dictionary<string, CharacterFlyweight> _flyweights = new Dictionary<string, CharacterFlyweight>();
public CharacterFlyweight GetFlyweight(char symbol, string fontFamily)
{
string key = $"{symbol}_{fontFamily}";
if (!_flyweights.ContainsKey(key))
{
_flyweights[key] = new CharacterFlyweight { Symbol = symbol, FontFamily = fontFamily };
}
return _flyweights[key];
}
}
7. Заместитель (Proxy)
Предоставляет суррогатный объект, контролирующий доступ к другому объекту. В C# особенно актуален для ленивой загрузки, кэширования и контроля доступа.
public interface IImage
{
void Display();
}
public class RealImage : IImage
{
private string _filename;
public RealImage(string filename)
{
_filename = filename;
LoadFromDisk();
}
private void LoadFromDisk()
{
Console.WriteLine($"Загрузка {_filename}");
}
public void Display()
{
Console.WriteLine($"Отображение {_filename}");
}
}
public class ImageProxy : IImage
{
private RealImage _realImage;
private string _filename;
public ImageProxy(string filename)
{
_filename = filename;
}
public void Display()
{
if (_realImage == null)
{
_realImage = new RealImage(_filename);
}
_realImage.Display();
}
}
Практическое применение в Backend-разработке на C#
В моей практике структурные паттерны играют ключевую роль:
- Адаптер и Фасад часто используются при интеграции микросервисов или работе с legacy-кодом.
- Декоратор идеален для добавления сквозной функциональности: логирование, кэширование, аутентификация.
- Заместитель реализуется через
IHttpClientFactoryдля управления HTTP-клиентами с политиками повторов. - Компоновщик полезен для построения сложных деревьев конфигураций или обработки бизнес-правил.
В современных C# фреймворках (ASP.NET Core) многие паттерны встроены "из коробки". Например, Middleware — это реализация Декоратора и Цепочки обязанностей, а Dependency Injection активно использует принципы Заместителя и Адаптера.
Важное замечание: Выбор паттерна должен определяться конкретной задачей, а не стремлением применить "модный" подход. Сверх-использование паттернов может привести к излишней сложности, тогда как их уместное применение значительно повышает поддерживаемость и тестируемость кода. В сочетании с SOLID-принципами, структурные паттерны помогают строить архитектуру, которая легко адаптируется к изменениям требований — что критически важно в долгосрочной backend-разработке.