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

В чем разница между 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();
    }
}

Ключевые черты

✅ Контролирует доступ
✅ Имеет ЖЕСТКИЙ интерфейс (то же, что реальный объект)
✅ Добавляет функциональность (логирование, кеширование, проверки)
✅ Клиент не знает, работает ли с прокси или реальным объектом

Сравнение таблица

АспектAdapterProxy
ЦельПреобразовать интерфейсКонтролировать доступ
Интерфейс клиентаМожет быть другойВСЕГДА одинаковый
Что обёртываетНесовместимый объектРеальный объект же класса
Чем клиент пользуетсяAdapter interfaceSubject 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)
В чем разница между Proxy и Adaptor? | PrepBro