Структурные паттерны (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.