Что такое паттерн проектирования «Прокси» (Proxy)?

«Что такое паттерн проектирования «Прокси» (Proxy)?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Прокси (Proxy) — это структурный паттерн, который предоставляет объект-заместитель или placeholder для другого объекта, контролируя доступ к нему. Прокси и реальный объект имеют одинаковый интерфейс, что позволяет клиенту работать с прокси так, как будто это реальный объект.

Зачем он нужен? Чтобы добавить дополнительный уровень косвенности для управления доступом, кэширования, логгирования, отложенной инициализации или защиты реального объекта.

Основные типы прокси:

  1. Виртуальный прокси (Lazy Initialization): Создает дорогой объект только по требованию.
  2. Защищающий прокси (Protection Proxy): Контролирует доступ к объекту на основе прав.
  3. Прокси удаленного доступа (Remote Proxy): Представляет объект, находящийся в другом адресном пространстве.
  4. Прокси для логирования/кэширования: Добавляет сквозную функциональность.

Пример на Java (Виртуальный прокси для тяжелого изображения):

// 1. Общий интерфейс
interface Image {
    void display();
}

// 2. Реальный объект (дорогой в создании)
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(); // Долгая операция
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 3. Прокси-заместитель
class ProxyImage implements Image {
    private RealImage realImage; // Ссылка на реальный объект
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        // Ленивая загрузка: создаем реальный объект только при первом вызове
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 4. Использование
public class Main {
    public static void main(String[] args) {
        Image image = new ProxyImage("high_res_photo.jpg");
        // RealImage еще НЕ создан

        System.out.println("Image object created. RealImage not loaded yet.");

        image.display(); // Первый вызов загружает и показывает
        // Loading image: high_res_photo.jpg
        // Displaying image: high_res_photo.jpg

        image.display(); // Второй вызов использует уже загруженный объект
        // Displaying image: high_res_photo.jpg
    }
}

Ключевые преимущества:

  • Контролируемый доступ. Можно добавить проверки до вызова реального объекта.
  • Ленивая инициализация. Откладывает создание ресурсоемких объектов.
  • Дополнительная логика. Легко добавить кэширование, логирование, безопасность.
  • Прозрачность для клиента. Клиентский код работает с интерфейсом, не зная о прокси.

Реализация в Java: Механизм динамических прокси (java.lang.reflect.Proxy) часто используется в AOP (Aspect-Oriented Programming) и фреймворках вроде Spring.