Перейти к содержанию

Структурные паттерны (Structural Patterns)

Какую проблему решает эта категория

Структурные паттерны отвечают на вопрос: как компоновать классы и объекты в более крупные структуры, не создавая между ними лишней жёсткой связанности.

Если порождающие паттерны говорят о том, как объект появляется на свет, то структурные - о том, как уже существующие объекты сочетаются друг с другом: как подружить два несовместимых интерфейса, как спрятать сложную подсистему за простым фасадом, как добавить объекту новую возможность, не трогая его класс, как построить дерево из однородных и составных элементов.

Общий приём почти всех паттернов этой категории - обёртывание (wrapping) или делегирование: вместо того чтобы менять существующий класс, мы создаём новый объект, который хранит ссылку на оригинальный и либо адаптирует его интерфейс, либо расширяет поведение, либо ограничивает доступ к нему.

Что объединяет паттерны из этой папки

Паттерн Ключевая идея
Adapter Преобразует интерфейс существующего класса в интерфейс, который ожидает клиент
Bridge Разделяет две независимые оси изменений на иерархии абстракции и реализации
Facade Прячет сложность подсистемы из многих классов за одним простым интерфейсом
Decorator Добавляет объекту новое поведение динамически, оборачивая его в другой объект с тем же интерфейсом
Composite Позволяет работать с одиночным объектом и деревом объектов единообразно
Flyweight Разделяет повторяющееся внутреннее состояние множества объектов для экономии памяти
Proxy Подставляет объект-заместитель, который контролирует доступ к реальному объекту (ленивая загрузка, кеширование, права доступа, логирование)

Когда об этой категории стоит вспомнить

  • Есть готовый класс (в том числе из сторонней библиотеки), но его интерфейс не подходит под то, что ожидает остальной код → Adapter.
  • Класс приходится расширять сразу по двум независимым измерениям, и число комбинаций подклассов растёт → Bridge.
  • Клиенту приходится работать напрямую с десятком взаимосвязанных классов подсистемы, хотя нужен только один сценарий использования → Facade.
  • Нужно добавить поведение (логирование, кеширование, валидацию) части объектов, но не всем экземплярам класса и без изменения самого класса → Decorator.
  • Данные естественно образуют иерархию "часть - целое" (файлы и папки, элементы UI, узлы дерева выражений) → Composite.
  • Миллионы похожих объектов расходуют память на одинаковые тяжёлые данные, которые можно сделать общими → Flyweight.
  • Нужно контролировать создание/доступ к "дорогому" или удалённому ресурсу, не меняя код, который с ним работает → Proxy.

Важная оговорка для .NET

Многие структурные паттерны в .NET настолько органично встроены в язык, что перестают восприниматься как "паттерн":

  • Decorator - это по сути то, как устроены Stream-обёртки (GZipStream вокруг FileStream, CryptoStream вокруг NetworkStream);
  • Proxy реализуется через DispatchProxy, RealProxy (legacy) или динамически генерируемые прокси в Entity Framework (lazy loading) и AOP-библиотеках (Castle DynamicProxy);
  • Facade часто выглядит просто как один сервисный класс с несколькими понятными публичными методами, за которым спрятаны детали работы с несколькими зависимостями.
  • Bridge часто проявляется как отделение предметной абстракции от провайдера инфраструктуры, а Flyweight - как фабрика разделяемых неизменяемых объектов по ключу.

Знание паттерна помогает узнать этот приём в чужом коде и осознанно применить его в своём, даже если итоговый код не выглядит как "учебная" реализация из GoF.