Adapter
Адаптер — это структурный паттерн, который позволяет объектам с несовместимыми интерфейсами работать вместе. Представьте, что вы приехали в страну с другим типом розеток. Чтобы зарядить ноутбук, вам нужен переходник. В коде роль переходника выполняет Адаптер.
Схема паттерна Adapter
classDiagram
direction LR
class Client {
+uses()
}
class TargetInterface {
<<Interface>>
+expected_method()
}
class Adapter {
+expected_method()
}
class LegacyClass {
+weird_method_name()
}
Client --> TargetInterface
Adapter ..|> TargetInterface
Adapter --> LegacyClass : calls weird_method_name()
Duck Typing
В статических языках Java, C++ адаптеры нужны постоянно, чтобы "подогнать" классы под строгие типы интерфейсов. В Python всё проще: если объект "крякает как утка" (имеет нужный метод), мы считаем его уткой.
Тем не менее, Адаптер полезен, когда вы используете стороннюю библиотеку, и имена её методов не совпадают с тем, что ожидает ваш код. Вместо того чтобы переписывать весь свой код, вы создаете класс-обертку.
Сильные стороны:
- Single Responsibility Principle: вы отделяете код конвертации данных от основной бизнес-логики.
- Open/Closed Principle: можно внедрять новые типы адаптеров без изменения существующего кода.
Decorator
Декоратор — это паттерн, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».
В отличие от наследования, где мы расширяем функциональность статически (создавая подкласс), декораторы позволяют делать это на лету и комбинировать разные возможности.
classDiagram
direction BT
%% Базовый интерфейс
class Notifier {
<<Interface>>
+send(message)
}
%% Реальный компонент
class EmailNotifier {
+send(message)
}
%% Базовый декоратор
class BaseDecorator {
-wrappee: Notifier
+send(message)
}
%% Конкретные декораторы
class SMSDecorator {
+send(message)
}
class SlackDecorator {
+send(message)
}
%% Связи
EmailNotifier ..|> Notifier
BaseDecorator ..|> Notifier
BaseDecorator o-- Notifier : Оборачивает (Has-A)
SMSDecorator --|> BaseDecorator
SlackDecorator --|> BaseDecorator
Встроенный синтаксис
Python — один из немногих языков, где Декоратор — это не просто паттерн из книги, а встроенная конструкция языка. Вы используете их каждый день:
@login_requiredв Django — проверяет авторизацию перед выполнением view-функции.@app.get("/")в FastAPI — регистрирует функцию как обработчик маршрута.@staticmethod— изменяет поведение метода внутри класса.
В Python декоратор — это просто функция, которая принимает другую функцию и возвращает новую (обернутую). Это намного элегантнее, чем создавать громоздкие классы-обертки, как это делается в Java.
Proxy
Заместитель — это объект, который выступает в роли «прослойки» между клиентом и реальным сервисным объектом. Прокси получает вызов от клиента, выполняет какую-то промежуточную работу (проверку доступа, кэширование, логгирование), а затем передает вызов реальному объекту.
classDiagram
direction LR
class Client
%% Интерфейс
class YouTubeLib {
<<Interface>>
+download_video(id)
}
%% Реальный сервис (Тяжелый)
class RealDownloader {
+download_video(id)
}
%% Прокси (Легкий / Умный)
class CacheProxy {
-real_service: RealDownloader
-cache: dict
+download_video(id)
}
Client --> YouTubeLib
RealDownloader ..|> YouTubeLib
CacheProxy ..|> YouTubeLib
%% Главная связь: Прокси управляет Реальным сервисом
CacheProxy --> RealDownloader : Делегирует, если нет в кэше
В Python вы часто встречаетесь с Proxy, даже не замечая этого:
- Lazy Loading в Django ORM: когда вы делаете запрос
user.groups.all(), Django не загружает данные из базы мгновенно. Он создает объект-прокси. Реальный запрос SQL выполняется только тогда, когда вы попытаетесь прочитать данные. - Mock-объекты в тестах: библиотека
unittest.mockсоздает прокси-объекты, которые притворяются реальными классами, чтобы вы могли изолированно протестировать код.
Виды прокси:
- Виртуальный прокси (Lazy Loading) откладывает создание тяжелого объекта до момента, когда он действительно понадобится.
- Кэширующий прокси хранит результаты вычислений и отдает их, если такой запрос уже был.
- Защищающий прокси проверяет права доступа перед тем, как пустить клиента к реальному объекту.
Bridge
Мост — это структурный паттерн, который предлагает разделить один большой класс или тесную иерархию классов на две отдельные иерархии — Абстракцию и Реализацию, которые можно развивать независимо друг от друга.
classDiagram
direction TB
class Shape {
-renderer: Renderer
+draw()
}
class Circle {
+draw()
}
class Square {
+draw()
}
class Renderer {
<<Interface>>
+render_circle()
+render_square()
}
class VectorRenderer {
+render_circle()
+render_square()
}
class RasterRenderer {
+render_circle()
+render_square()
}
Circle --|> Shape
Square --|> Shape
VectorRenderer ..|> Renderer
RasterRenderer ..|> Renderer
Shape o-- Renderer : Агрегация
Классический пример: у нас есть класс Фигура (Круг, Квадрат) и способ её рисования (Векторный, Растровый). Если использовать обычное наследование, нам придется создавать классы: VectorCircle, RasterCircle, VectorSquare, RasterSquare. Количество классов растет как произведение $N \times M$.
Паттерн Мост предлагает не наследовать способ рисования, а использовать композицию. Мы передаем объект Renderer внутри Shape. Теперь мы можем добавлять новые фигуры и новые способы рендеринга независимо друг от друга.
В Python это часто реализуется через внедрение зависимостей (Dependency Injection): мы просто передаем нужную реализацию в конструктор класса.
Facade
Фасад — это паттерн, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.
classDiagram
direction LR
class Client
class Facade {
+simple_method()
}
class ComplexSystemA
class ComplexSystemB
class ComplexSystemC
Client --> Facade
Facade --> ComplexSystemA
Facade --> ComplexSystemB
Facade --> ComplexSystemC
Вы пользуетесь фасадами каждый день. Отличный пример — библиотека Requests в Python.
Внутри Python есть стандартный модуль urllib3. Он мощный, но очень сложный и низкоуровневый. Библиотека requests — это Фасад, который предоставляет вам простой и понятный метод requests.get(), скрывая внутри себя сотни строк кода настройки соединений, кодировок и заголовков.
Когда применять:
- Когда нужно предоставить простой интерфейс к сложной подсистеме.
- Когда вы хотите разложить подсистему на слои (Фасад может служить точкой входа для определенного уровня приложения).
Composite
Компоновщик — это структурный паттерн, который позволяет сгруппировать множество объектов в древовидную структуру, а затем работать с ней так, как будто это единичный объект.
classDiagram
direction BT
class FileSystemComponent {
<<Interface>>
+show_details()
}
class File {
+show_details()
}
class Folder {
-children: list
+add(component)
+show_details()
}
File ..|> FileSystemComponent
Folder ..|> FileSystemComponent
Folder o-- FileSystemComponent : Содержит
Лучший пример — файловая система. У вас есть «Файлы» простые объекты и «Папки» составные объекты. Папка может содержать файлы и другие папки. Но для пользователя (или операционной системы) и папка, и файл — это просто «иконка», которую можно открыть, переименовать или переместить.
Суть паттерна:
Создается общий интерфейс Component. Лист (Leaf) реализует его напрямую. Контейнер (Composite) реализует его, пробегая циклом по всем своим детям и вызывая этот метод у них.
В веб-разработке Composite часто встречается при создании меню или при рендеринге HTML.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ