← Назад к вопросам

Можно ли класс А проксировать классом В?

2.0 Middle🔥 121 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, класс А можно проксировать классом В. Это распространённый паттерн

Proxy паттерн — это структурный паттерн проектирования, который позволяет предоставить замену (суррогат) для другого объекта, контролируя доступ к нему. Класс В выступает как посредник для класса А.

Концепция

Проксирование означает, что класс В:

  • Скрывает экземпляр класса А
  • Делегирует вызовы методов к А
  • Добавляет функциональность (логирование, кеширование, контроль доступа)
  • Реализует тот же интерфейс, что и А

Простой пример: Static Proxy

// Интерфейс, который будут реализовывать оба класса
public interface DataService {
    String getData();
    void saveData(String data);
}

// Реальный класс А
public class RealDataService implements DataService {
    @Override
    public String getData() {
        // Дорогостоящая операция (БД запрос)
        System.out.println("Читаю из БД...");
        return "Важные данные";
    }

    @Override
    public void saveData(String data) {
        System.out.println("Сохраняю в БД: " + data);
    }
}

// Прокси класс В
public class DataServiceProxy implements DataService {
    private RealDataService realService;  // Ссылка на А
    private String cachedData;            // Добавляем функциональность

    public DataServiceProxy() {
        this.realService = new RealDataService();
    }

    @Override
    public String getData() {
        // Добавляем кеширование
        if (cachedData == null) {
            System.out.println("Кеш пуст, запрашиваю данные...");
            cachedData = realService.getData();
        } else {
            System.out.println("Возвращаю из кеша");
        }
        return cachedData;
    }

    @Override
    public void saveData(String data) {
        System.out.println("[ПРОКСИ] Логирую сохранение: " + data);
        realService.saveData(data);
        cachedData = null;  // Инвалидируем кеш
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        DataService service = new DataServiceProxy();
        
        System.out.println(service.getData());  // Кеш пуст, запрашиваю...
        System.out.println(service.getData());  // Возвращаю из кеша
        
        service.saveData("Новые данные");
        System.out.println(service.getData());  // Кеш пуст, запрашиваю...
    }
}

Вывод:

Кеш пуст, запрашиваю данные...
Читаю из БД...
Важные данные
Возвращаю из кеша
Важные данные
[ПРОКСИ] Логирую сохранение: Новые данные
Сохраняю в БД: Новые данные
Кеш пуст, запрашиваю данные...
Читаю из БД...
Важные данные

Dynamic Proxy (Отражение)

Для более гибкого подхода используй JDK Dynamic Proxy:

public class LoggingInvocationHandler implements InvocationHandler {
    private Object realObject;  // Объект А

    public LoggingInvocationHandler(Object realObject) {
        this.realObject = realObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[LOG] Вызов метода: " + method.getName());
        
        // Делегируем реальному объекту
        Object result = method.invoke(realObject, args);
        
        System.out.println("[LOG] Метод завершён: " + method.getName());
        return result;
    }
}

// Использование
DataService realService = new RealDataService();

DataService proxy = (DataService) Proxy.newProxyInstance(
    DataService.class.getClassLoader(),
    new Class[]{DataService.class},
    new LoggingInvocationHandler(realService)
);

proxy.getData();  // [LOG] Вызов метода: getData...

Когда использовать Proxy

СценарийОписание
Lazy LoadingСоздавать объект только когда он действительно нужен
Access ControlПроверка прав доступа перед вызовом метода
Logging/MonitoringЛогирование вызовов и времени выполнения
CachingКешировать результаты дорогостоящих операций
Remote ObjectsSpring AOP, RMI, веб-сервисы

Static vs Dynamic Proxy

ТипПлюсыМинусы
StaticПростой, быстрый, понятныйНужно писать для каждого класса
DynamicУниверсальный, переиспользуемыйМедленнее, сложнее отладка

Вывод

Да, класс В может проксировать класс А. В Java это делается:

  1. Static Proxy — явное написание класса-посредника
  2. Dynamic Proxy — через Proxy.newProxyInstance() и InvocationHandler
  3. CGLIB/ByteBuddy — для проксирования классов без интерфейсов (Spring AOP)
  4. Annotation-based — через @Transactional, @Cacheable (Spring Framework)