Proxy (Заместитель)¶
Категория: структурный паттерн.
Проблема¶
Нужно контролировать доступ к объекту, не меняя код, который с ним работает, - например: - объект дорого создавать, и хочется отложить это до момента реального использования (ленивая инициализация); - объект находится на удалённом сервере, и вызов к нему на самом деле должен пройти по сети; - нужно закешировать результаты, проверить права доступа или залогировать обращения перед тем, как выполнить реальный вызов.
Встраивать эту логику прямо в реальный класс - значит смешивать его основную ответственность с побочными задачами (кеширование, безопасность, логирование).
Решение¶
Создаётся класс-заместитель (Proxy), который:
1. Реализует тот же интерфейс, что и реальный объект (RealSubject).
2. Хранит ссылку на реальный объект (создавая его сразу, лениво или обращаясь к нему удалённо).
3. Перед или вместо вызова реального объекта выполняет дополнительную логику: проверку, кеширование, логирование, отложенное создание.
Клиентский код работает с прокси через общий интерфейс и не обязан знать, что перед ним не реальный объект, а его заместитель.
Виды Proxy¶
- Virtual Proxy - откладывает создание "дорогого" объекта до первого реального обращения (ленивая инициализация).
- Remote Proxy - представляет объект, который физически находится в другом процессе или на другой машине, скрывая детали сетевого вызова.
- Protection Proxy - проверяет права доступа перед тем, как разрешить вызов реального объекта.
- Caching Proxy - кеширует результаты вызовов к реальному объекту, чтобы не выполнять их повторно.
- Logging/Smart Reference Proxy - добавляет учёт вызовов, подсчёт ссылок, логирование.
Структура¶
Subject- общий интерфейсRealSubjectиProxy.RealSubject- объект, доступ к которому контролируется.Proxy- хранит ссылку наRealSubject, реализуетSubject, добавляет свою логику вокруг вызовов.
Когда применять¶
- Создание реального объекта дорогое, а нужен он не всегда - есть смысл отложить создание до первого использования.
- Нужно добавить контроль доступа, кеширование или логирование к объекту, не изменяя его код.
- Реальный объект находится в другом процессе, домене или на другом сервере.
Плюсы¶
- Позволяет управлять жизненным циклом и доступом к объекту, не трогая ни его код, ни код клиента (оба продолжают работать с общим интерфейсом).
- Ленивая инициализация через Virtual Proxy экономит ресурсы, если объект нужен не при каждом запуске программы.
Минусы¶
- Добавляет ещё один уровень косвенности, что может затруднять отладку и понимание, где именно выполняется реальная работа.
- Если прокси реализован небрежно (например, забыли делегировать какой-то метод интерфейса), поведение через прокси может незаметно отличаться от поведения реального объекта.
Отличие от Decorator¶
Оба паттерна структурно выглядят одинаково - обёртка за тем же интерфейсом. Но цель разная: Proxy контролирует доступ к объекту (может вообще не выполнить реальный вызов, если, например, данные уже в кеше или доступ запрещён), а Decorator добавляет новую функциональность, всегда делегируя вызов дальше и расширяя результат.
Пример в .NET Framework / BCL¶
- Lazy loading навигационных свойств в Entity Framework - EF Core на лету генерирует прокси-класс поверх сущности, который подгружает связанные данные при первом обращении к свойству-навигации.
System.Runtime.Remoting.Proxies.RealProxy(legacy .NET Remoting) - явная реализация Remote Proxy.DispatchProxyвSystem.Reflection- базовый класс для построения собственных динамических прокси (часто используется в AOP-библиотеках и мокинг-фреймворках).Lazy<T>можно рассматривать как частный случай идеи Virtual Proxy применительно к одному значению.