Структурные паттерны: Adapter, Decorator, Facade, Proxy, Composite
↑ 8.1 Принципы и паттерны проектирования в C♯ · ← Предыдущая · → Следующая
Зачем это на собесе
Ждут отличий 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, если не ограничивать ответственность.
- Сквозные аспекты (логирование, кэш) удобны как декораторы, а не в бизнес-коде.
Практика
- Добавьте кэширующий декоратор к репозиторию через Scrutor.
- Напишите адаптер к внешнему API под свой интерфейс.
- Сделайте Composite для иерархии прав/меню.
Вопросы с ответами
Чем Decorator отличается от Proxy?
Декоратор добавляет функциональность, прокси контролирует доступ к объекту (ленивость, права, кэш); оба сохраняют интерфейс.
Adapter или Facade?
Adapter приводит один интерфейс к другому, Facade упрощает работу с подсистемой.