← Назад к вопросам
В чем разница между Proxy и Adaptor?
2.0 Middle🔥 161 комментариев
#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Proxy vs Adapter паттерны
Это часто путают, потому что оба используют обёртку (wrapper). Но они решают разные задачи.
Adapter (Адаптер)
Назначение
Адаптер ПРЕОБРАЗУЕТ интерфейс
Делает несовместимое совместимым
Пример: USB адаптер
// Старый интерфейс (USB 2.0)
public interface USBDevice {
void connect();
byte[] readData();
}
// Новый интерфейс (USB 3.0)
public interface USB3Device {
void plugIn();
ByteBuffer getData();
}
// Адаптер преобразует старое в новое
public class USB2to3Adapter implements USB3Device {
private USBDevice oldDevice;
public USB2to3Adapter(USBDevice device) {
this.oldDevice = device;
}
@Override
public void plugIn() {
oldDevice.connect(); // Преобразуем вызов
}
@Override
public ByteBuffer getData() {
byte[] data = oldDevice.readData();
return ByteBuffer.wrap(data); // Преобразуем результат
}
}
Ключевые черты
✅ Преобразует интерфейс
✅ Работает с несовместимыми объектами
✅ Часто меняет сигнатуру методов
✅ Для интеграции старого кода
Proxy (Прокси)
Назначение
Прокси КОНТРОЛИРУЕТ доступ к объекту
Лежит между клиентом и реальным объектом
Пример: Защита от несанкционированного доступа
// Реальный сервис
public interface BankAccount {
void withdraw(int amount);
int getBalance();
}
public class RealBankAccount implements BankAccount {
private int balance = 1000;
@Override
public void withdraw(int amount) {
balance -= amount; // Просто снимаем
}
@Override
public int getBalance() {
return balance;
}
}
// Прокси добавляет контроль доступа
public class BankAccountProxy implements BankAccount {
private RealBankAccount realAccount;
private String userId;
private boolean isAuthenticated;
public BankAccountProxy(RealBankAccount account, String userId) {
this.realAccount = account;
this.userId = userId;
this.isAuthenticated = false;
}
public void authenticate(String password) {
// Проверяем пароль
if ("correct_password".equals(password)) {
isAuthenticated = true;
} else {
throw new SecurityException("Unauthorized");
}
}
@Override
public void withdraw(int amount) {
if (!isAuthenticated) {
throw new SecurityException("Not authenticated");
}
if (amount > realAccount.getBalance()) {
throw new IllegalArgumentException("Insufficient funds");
}
realAccount.withdraw(amount); // Делегируем реальному объекту
}
@Override
public int getBalance() {
if (!isAuthenticated) {
throw new SecurityException("Not authenticated");
}
return realAccount.getBalance();
}
}
Ключевые черты
✅ Контролирует доступ
✅ Имеет ЖЕСТКИЙ интерфейс (то же, что реальный объект)
✅ Добавляет функциональность (логирование, кеширование, проверки)
✅ Клиент не знает, работает ли с прокси или реальным объектом
Сравнение таблица
| Аспект | Adapter | Proxy |
|---|---|---|
| Цель | Преобразовать интерфейс | Контролировать доступ |
| Интерфейс клиента | Может быть другой | ВСЕГДА одинаковый |
| Что обёртывает | Несовместимый объект | Реальный объект же класса |
| Чем клиент пользуется | Adapter interface | Subject interface |
| Когда создаётся | При интеграции legacy | Всегда |
| Пример | USB адаптер | Bodyguard (охранник) |
Типы Proxy
1. Protection Proxy (Защита)
public class DocumentProxy implements Document {
private Document realDocument;
private User currentUser;
public void read() {
if (currentUser.hasPermission("READ")) {
realDocument.read(); // Только авторизованные
} else {
throw new AccessDeniedException();
}
}
}
2. Virtual Proxy (Ленивая загрузка)
public class ImageProxy implements Image {
private String filename;
private Image realImage; // Не загружается сразу
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // Загружаем только при display()
}
realImage.display();
}
}
3. Logging Proxy (Логирование)
public class LoggingUserRepository implements UserRepository {
private UserRepository realRepository;
public User findById(Long id) {
long start = System.currentTimeMillis();
logger.info("Finding user: " + id);
User result = realRepository.findById(id);
long duration = System.currentTimeMillis() - start;
logger.info("User found. Duration: " + duration + "ms");
return result;
}
}
4. Caching Proxy (Кеширование)
public class CachingUserRepository implements UserRepository {
private UserRepository realRepository;
private Map<Long, User> cache = new HashMap<>();
public User findById(Long id) {
if (cache.containsKey(id)) {
logger.info("Cache hit: " + id);
return cache.get(id);
}
logger.info("Cache miss: " + id);
User user = realRepository.findById(id);
cache.put(id, user);
return user;
}
}
Практический пример: Различия
Adapter
// РАЗНЫЕ интерфейсы
public interface OldPaymentGateway {
boolean doPayment(double amount);
}
public interface NewPaymentGateway {
PaymentResult processPayment(BigDecimal amount);
}
// Адаптер ПРЕОБРАЗУЕТ
public class PaymentGatewayAdapter implements NewPaymentGateway {
private OldPaymentGateway oldGateway;
@Override
public PaymentResult processPayment(BigDecimal amount) {
boolean success = oldGateway.doPayment(amount.doubleValue());
return new PaymentResult(success); // Преобразование
}
}
Proxy
// ОДИНАКОВЫЕ интерфейсы
public interface PaymentGateway {
PaymentResult processPayment(BigDecimal amount);
}
// Прокси КОНТРОЛИРУЕТ
public class PaymentGatewayProxy implements PaymentGateway {
private PaymentGateway realGateway;
@Override
public PaymentResult processPayment(BigDecimal amount) {
// Контроль доступа
if (!isAuthenticated()) {
throw new SecurityException();
}
// Логирование
logger.info("Processing payment: " + amount);
// Кеширование
if (cache.containsKey(amount)) {
return cache.get(amount);
}
// Делегируем реальному
PaymentResult result = realGateway.processPayment(amount);
cache.put(amount, result);
return result;
}
}
Dynamic Proxy (Java)
Java имеет встроенный Proxy
// Создаём proxy для любого интерфейса
public class LoggingInvocationHandler implements InvocationHandler {
private Object target;
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("Calling: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long duration = System.currentTimeMillis() - start;
logger.info("Completed in " + duration + "ms");
return result;
}
}
// Использование
UserRepository repo = new UserRepositoryImpl();
UserRepository proxy = (UserRepository) Proxy.newProxyInstance(
UserRepository.class.getClassLoader(),
new Class[]{UserRepository.class},
new LoggingInvocationHandler(repo)
);
proxy.findById(1L); // Автоматически логируется
Когда использовать
Adapter
✅ Интеграция несовместимого кода
✅ Обёртывание legacy system
✅ Преобразование данных между API
✅ Bridge между старым и новым
Proxy
✅ Контроль доступа (Security)
✅ Логирование
✅ Кеширование
✅ Ленивая загрузка
✅ Счётчики (counting calls)
✅ Транзакции
Реальные примеры в Spring
Adapter
// Spring HandlerAdapter — адаптирует разные контроллеры
public class RequestMappingHandlerAdapter implements HandlerAdapter {
public void handle(HttpServletRequest request) {
// Адаптирует любой контроллер к одному интерфейсу
}
}
Proxy
// Spring AOP создаёт прокси для @Transactional
@Service
public class UserService {
@Transactional // Spring создаёт прокси!
public void updateUser(User user) {
// Прокси добавит управление транзакциями
}
}
// Аналогично @Cacheable, @Async
Итог
Adapter (Адаптер):
- ПРЕОБРАЗУЕТ интерфейс
- Работает с несовместимым
- Меняет сигнатуру методов
- Для интеграции
Proxy (Прокси):
- КОНТРОЛИРУЕТ доступ
- Сохраняет интерфейс
- Добавляет функциональность
- Клиент не знает о прокси
Просто запомни:
Adapter = переходник (USB-C to USB)
Proxy = охранник (bodyguard)