Что это такое
Паттерн Proxy ставит перед настоящим объектом заместителя с тем же интерфейсом. Вызывающий код обращается к заместителю так, будто это и есть настоящий объект; заместитель сам решает, когда и как задействовать оригинал.
Обычные задачи такого заместителя: кэширование ответов, проверка прав доступа, откладывание дорогого создания объекта, подсчёт вызовов.
Примечание: называйте класс по его задаче — CachedWeather, ReadOnlyAccount — но никогда не Proxy. Это имя уже занято встроенным объектом JavaScript.
Пример: кэширующий заместитель
Настоящий сервис погоды работает медленно. Заместитель отвечает на повторные вопросы из своего кэша Map и обращается к сервису только за новыми — и всё это за тем же самым интерфейсом Weather.
Заместитель, который запоминает
Первый вопрос про Paris доходит до удалённого сервиса; на второй отвечает кэш — строка (обращаемся к удалённому сервису...) печатается только один раз, а второй ответ заканчивается на (из кэша).
Вывод: программа на TypeScript и захваченный вывод
interface Weather {
forecast(city: string): string
}
// Настоящий сервис работает медленно и обходится дорого.
class WeatherService implements Weather {
forecast(city: string): string {
console.log(" (обращаемся к удалённому сервису...)")
return "солнечно в " + city
}
}
// Заместитель стоит перед сервисом и кэширует ответы.
class CachedWeather implements Weather {
private service: Weather
private cache = new Map<string, string>()
constructor(service: Weather) {
this.service = service
}
forecast(city: string): string {
const cached = this.cache.get(city)
if (cached !== undefined) {
return cached + " (из кэша)"
}
const answer = this.service.forecast(city)
this.cache.set(city, answer)
return answer
}
}
const weather: Weather = new CachedWeather(new WeatherService())
console.log(weather.forecast("Paris"))
console.log(weather.forecast("Paris"))
Когда его использовать
Используйте его, чтобы добавить кэширование, контроль доступа, ленивую загрузку или учёт вызовов перед тем, что вы не можете или не должны менять — особенно перед удалёнными или дорогими ресурсами.
Откажитесь от него, когда объект принадлежит вам и поведение должно находиться внутри него. Proxy нужен для управления доступом; Decorator — для добавления поведения слоями; выбирайте тот замысел, который подходит.
Плюсы и минусы
Плюсы: вызывающему коду вообще не нужно меняться — интерфейс остаётся прежним; дорогая работа пропускается или контролируется в одном месте.
Минусы: скрытое поведение — вызывающий код может получить ответ из кэша, не зная об этом; устаревший кэш и неожиданные отказы приходится отлаживать за внешне безобидным интерфейсом.