Структурные паттерны: Adapter, Decorator, Facade, Proxy, Composite

↑ 8.1 Принципы и паттерны проектирования в C♯ · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

Ждут отличий Adapter, Decorator и Proxy — их часто путают.

Объяснение

ПаттернЦельПример в .NET
Adapterприспособить чужой интерфейс к нужномуобёртка над SDK внешней системы (ACL)
Decoratorдобавить поведение, не меняя класс, тем же интерфейсомкэширующий/логирующий репозиторий, Stream (GZipStream)
Facadeпростой интерфейс к сложной подсистемесервис-оркестратор заказов
Proxyзаместитель контролирует доступ (ленивая загрузка, кэш, права)прокси EF для lazy loading, gRPC-клиент
Compositeдревовидная структура с единым интерфейсомдерево меню, IServiceProvider-иерархия, выражения
Bridgeразвязать абстракцию и реализациюдрайверы + логика
Flyweightразделяемые общие данныеstring interning
// Decorator: кэш поверх репозитория без изменения клиента
public class CachedProducts(IProducts inner, IMemoryCache cache) : IProducts
{
    public Task<Product?> GetAsync(int id, CancellationToken ct) =>
        cache.GetOrCreateAsync($"p:{id}", _ => inner.GetAsync(id, ct))!;
}
services.AddScoped<IProducts, SqlProducts>();
services.Decorate<IProducts, CachedProducts>();      // Scrutor
 
// Adapter: внешний клиент → наш порт
public class AcmeGatewayAdapter(AcmeSdk sdk) : IPaymentGateway
{
    public async Task<PaymentResult> ChargeAsync(Charge c, CancellationToken ct) => Map(await sdk.CreateChargeAsync(Map(c), ct));
}

Различия: Adapter меняет интерфейс, Decorator сохраняет интерфейс и добавляет поведение, Proxy сохраняет интерфейс и управляет доступом, Facade упрощает набор интерфейсов.

Нюансы и подводные камни

  • Цепочки декораторов усложняют отладку: порядок важен.
  • Прокси и декораторы должны сохранять контракт (LSP).
  • Facade превращается в God object, если не ограничивать ответственность.
  • Сквозные аспекты (логирование, кэш) удобны как декораторы, а не в бизнес-коде.

Практика

  1. Добавьте кэширующий декоратор к репозиторию через Scrutor.
  2. Напишите адаптер к внешнему API под свой интерфейс.
  3. Сделайте Composite для иерархии прав/меню.

Вопросы с ответами

Связанные темы