Что это такое
Паттерн Заместитель (Proxy) ставит перед настоящим объектом подставного с тем же интерфейсом. Вызывающий код обращается к заместителю как к настоящему объекту; заместитель сам решает, когда и как задействовать оригинал.
Обычные задачи заместителя: кэширование ответов, проверка прав доступа, отложенное создание дорогого объекта, подсчёт вызовов.
Пример: кэширующий заместитель
Настоящий сервис погоды работает медленно. Заместитель отвечает на повторные запросы из своего кэша и обращается к сервису только за новыми данными — за тем же самым интерфейсом.
Подставной объект, который помнит
Первый запрос о городе Paris доходит до удалённого сервиса, второй берётся из кэша — строка «обращаемся к удалённому сервису» печатается только один раз. Одна проверка isset по массиву кэша решает, каким путём пойдёт запрос.
Вывод: PHP-скрипт
<?php
interface Weather {
public function forecast(string $city): string;
}
// Настоящий сервис медленный и дорогой.
class WeatherService implements Weather {
public function forecast(string $city): string {
echo " (обращаемся к удалённому сервису...)\n";
return "солнечно в " . $city;
}
}
// Заместитель стоит впереди и кэширует ответы.
class CachedWeather implements Weather {
/** @var array<string, string> */
private array $cache = [];
public function __construct(private WeatherService $service) {}
public function forecast(string $city): string {
if (isset($this->cache[$city])) {
return $this->cache[$city] . " (из кэша)";
}
$answer = $this->service->forecast($city);
$this->cache[$city] = $answer;
return $answer;
}
}
$w = new CachedWeather(new WeatherService());
echo $w->forecast("Paris"), "\n";
echo $w->forecast("Paris"), "\n";
Когда использовать
Используйте, чтобы добавить кэширование, контроль доступа, отложенную загрузку или учёт вызовов перед тем, что вы не можете или не должны менять — особенно перед удалёнными или дорогими ресурсами.
Не используйте, когда объект ваш и поведение должно находиться внутри него. Заместитель управляет доступом; декоратор добавляет поведение слоями — выбирайте по замыслу.
Плюсы и минусы
Плюсы: вызывающий код вообще не нужно менять; дорогая работа пропускается или контролируется в одном месте.
Минусы: скрытое поведение — вызывающий код может получить ответ из кэша, не зная об этом; устаревший кэш и неожиданные отказы приходится отлаживать за безобидным с виду интерфейсом.