Прокси предназначен для управления доступом к заданному объекту, перехватывая все вызовы к нему и прозрачно замещая его. Ни интерфейс ни функциональность замещенного компонента, с точки зрения клиента, не изменяются.
Данный шаблон используется если:
- работа с объектом не должна зависеть от того, где он реально расположен (от адресного пространства приложения до удаленного сервера);
- (или) нужно выполнять определенные действия при доступе к объекту;
- (или) необходимо оптимизировать взаимодействие объекта с клиентом.
Таким образом, задача Прокси – упростить и/или оптимизировать взаимодействие с объектом, скрывая несущественные для конкретной задачи подробности реализации. При этом он прозрачен для клиента, поскольку предоставляет тот же интерфейс, что и замещаемый объект.
Реализация шаблона может как порождать экземпляр замещаемого объекта, так и ссылаться на уже существующий. Независимо от этого, Прокси контролирует все взаимодействия с объектом, а в некоторых случаях может управлять его существованием.
В зависимости от цели использования можно выделить следующие типы Прокси:
- Удаленный прокси (Remote proxy) – обеспечивает взаимодействие с объектом в другом адресном пространстве. Это может быть как другое приложение на текущем компьютере, так и компонент на интернет сервере. При этом клиент обращается к объекту как к локальному, не замечая различий.
- Виртуальный прокси (Virtual proxy) – выполняет оптимизацию доступа к объекту, может самостоятельно обрабатывать некоторые запросы. Например, создание ресурсоёмких объектов только при абсолютной необходимости в них. Другим характерным примером может служить кэширование, в результате которого Прокси сам может отвечать на часть запросов.
- Защищающий прокси (Protection proxy) – управляет объектом, разграничивая права доступа различных клиентов.
- Умная ссылка (Smart reference) – обеспечивает выполнение дополнительных действий при вызове методов объекта. Примерами могут служить подсчет ссылок или обеспечение потокобезопасности работы с объектом.
Разрабатываемый для конкретной задачи Прокси, может сочетать в себе несколько типов. Например Прокси, обеспечивающий доступ к компоненту на сервере (удаленный), кэширующий часть запросов к нему (виртуальный), блокирующий некоторые методы для разных типов клиентов (защищающий) и учитывающий их число (умная ссылка).
Особенности использования
Хорошей практикой порождения экземпляра Прокси является использование Фабричного метода или Абстрактной фабрики. Клиент в этом случае не знает с какой реализацией интерфейса он работает: с реальным объектом или с Прокси. Это обеспечивает больший уровень прозрачности применения шаблона.
Стоит отметить, что шаблон позволяет использовать интерфейс для взаимодействия с замещаемым объектом. В этом случае конкретная реализация скрыта от Прокси и он может работать практически со всеми объектами, поддерживающими данный интерфейс.
Шаблон не накладывает ограничения на вложения одного Прокси в другой. Такой подход позволяет строить их различные конфигурации в процессе работы программы.
Интересный вариант применения шаблона – копирование по требованию. Его идея заключается в следующем: копирование тяжелых объектов всегда ресурсоемкая операция. Но иногда копия на протяжении части или даже всего своего существования не отличается от оригинала. В этом случае можно отложить реальное копирование данных до момента первой модификации копии или оригинала. Это может сэкономить ресурсы при использовании больших объектов.
Реализация шаблона в общем виде
- создаем класс, реализующий интерфейс замещаемого объекта, который: создает сам замещаемый объект или получает ссылку на его экземпляр;
- добавляем необходимую функциональность в методы Прокси, в зависимости от задачи;
- используем порождающие шаблоны большей прозрачности Прокси для клиента;
- клиент, не зная использует ли он Прокси или оригинальный объект, одинаково взаимодействует с переданным ему экземпляром.
Пример реализации
Рассмотрим реализацию на примере имитации работы web браузера.
Создадим интерфейс ISite.
Реализуем данный интерфейс в классе Site.
Так же реализуем интерфейс ISite в классе SiteProxy.
Посмотрим применение.