В чем разница между паттернами Proxy и Decorator
Что проверяют
Видите ли вы разницу в намерении при почти одинаковой структуре: оба паттерна реализуют тот же интерфейс и держат ссылку на объект. Следом обычно просят назвать, где каждый встречается в JDK и Spring, и объяснить, почему @Transactional не срабатывает при вызове метода изнутри того же бина.
Как отвечать
Начните со сходства, а потом разведите по смыслу:
- Proxy: контролирует доступ к объекту, не меняя его поведения для клиента. Типичные задачи: ленивое создание, проверка прав, кеширование, удалённый вызов, транзакции. Клиент обычно не знает, что работает с заместителем;
- Decorator: добавляет объекту новые обязанности, и клиент сам решает, чем обернуть. Декораторы складываются в цепочку, порядок имеет значение;
- кто создаёт обёртку: заместителя чаще подставляет фреймворк или фабрика, а декоратор собирает клиент, передавая исходный объект в конструктор;
- жизненный цикл: прокси может сам создавать реальный объект или откладывать его создание, декоратор всегда получает готовый.
Примеры из Java. Декоратор: java.io, где new BufferedReader(new InputStreamReader(in)) добавляет возможности слой за слоем, и Collections.synchronizedList. Прокси: java.lang.reflect.Proxy с InvocationHandler, прокси Spring AOP для @Transactional и @Cacheable (JDK dynamic proxy по интерфейсу или CGLIB-подкласс), ленивые прокси Hibernate для связей с FetchType.LAZY.
На уточнение про @Transactional ответ такой: вызов this.method() идёт в сам объект, минуя прокси, поэтому транзакция не открывается.
Где спотыкаются
Приводят в пример прокси-сервер в сети. Это сетевой узел, а не паттерн проектирования, и интервьюер ждёт пример из кода.
Различают паттерны по структуре. Структурно они почти одинаковы, отличие в намерении: контроль доступа против добавления поведения.
Не могут объяснить, откуда берётся LazyInitializationException в Hibernate: прокси пытается загрузить данные после закрытия сессии. Знание паттерна без его проявлений в реальном стеке выглядит заученным.