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

В чем разница между шаблонами проектирования Adapter, Facade и Proxy?

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

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

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

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

Разница между шаблонами Adapter, Facade и Proxy

Эти три паттерна часто путают, так как они похожи на первый взгляд - все используют обёртку. Однако их цели и использование принципиально различаются.

Adapter (Адаптер)

Цель: Сделать НЕСОВМЕСТИМЫЕ интерфейсы совместимыми. Конвертация интерфейса одного класса в интерфейс другого, который ожидает клиент.

// Старый класс - несовместимый интерфейс
public class OldPaymentGateway {
    public void sendMoneyXML(String xmlData) {
        // отправляет платёж в формате XML
    }
}

// Новый интерфейс, который ожидает система
public interface ModernPaymentGateway {
    void processPayment(PaymentRequest request);
}

// Adapter - преобразует старый интерфейс в новый
public class PaymentGatewayAdapter implements ModernPaymentGateway {
    private OldPaymentGateway oldGateway;
    
    public PaymentGatewayAdapter(OldPaymentGateway oldGateway) {
        this.oldGateway = oldGateway;
    }
    
    @Override
    public void processPayment(PaymentRequest request) {
        // Адаптируем новый интерфейс к старому
        String xmlData = convertToXML(request);
        oldGateway.sendMoneyXML(xmlData);
    }
    
    private String convertToXML(PaymentRequest request) {
        // Логика конвертации
        return "<?xml version=\"1.0\"?>...";
    }
}

// Использование
OldPaymentGateway oldGateway = new OldPaymentGateway();
ModernPaymentGateway adapter = new PaymentGatewayAdapter(oldGateway);

PaymentRequest request = new PaymentRequest(100, "USD");
adapter.processPayment(request); // Работает, несмотря на несовместимость

Задача Adapter:

  • Интеграция с legacy кодом
  • Использование библиотек с несовместимыми интерфейсами
  • Миграция старого кода на новый
  • Adapter работает как "переводчик"

Facade (Фасад)

Цель: Предоставить УПРОЩЁННЫЙ интерфейс к СЛОЖНОЙ подсистеме. Скрыть сложность и предоставить единую точку доступа.

// Сложная подсистема с множеством классов
public class PaymentProcessor {
    public void processPayment(Payment payment) { /* ... */ }
}

public class FraudDetector {
    public boolean isFraud(Payment payment) { /* ... */ }
}

public class NotificationService {
    public void notifyUser(User user, String message) { /* ... */ }
}

public class AccountingService {
    public void recordTransaction(Payment payment) { /* ... */ }
}

public class ReportingService {
    public void generateReport(Payment payment) { /* ... */ }
}

// Facade - упрощает работу с подсистемой
public class PaymentFacade {
    private PaymentProcessor processor;
    private FraudDetector fraudDetector;
    private NotificationService notifier;
    private AccountingService accounting;
    private ReportingService reporting;
    
    public PaymentFacade() {
        this.processor = new PaymentProcessor();
        this.fraudDetector = new FraudDetector();
        this.notifier = new NotificationService();
        this.accounting = new AccountingService();
        this.reporting = new ReportingService();
    }
    
    /**
     * Простой метод, скрывающий сложность
     */
    public void executePayment(User user, Payment payment) {
        // Проверка мошенничества
        if (fraudDetector.isFraud(payment)) {
            throw new PaymentException("Suspicious transaction");
        }
        
        // Обработка платежа
        processor.processPayment(payment);
        
        // Уведомление пользователя
        notifier.notifyUser(user, "Payment successful");
        
        // Запись в учёт
        accounting.recordTransaction(payment);
        
        // Отчётность
        reporting.generateReport(payment);
    }
}

// Использование - очень просто!
PaymentFacade facade = new PaymentFacade();
facade.executePayment(user, payment);

Задача Facade:

  • Скрыть сложность подсистемы
  • Предоставить простой интерфейс
  • Слабая связанность между компонентами
  • Facade инкапсулирует целую подсистему

Proxy (Прокси)

Цель: Контролировать ДОСТУП к реальному объекту. Замещает другой объект и перехватывает вызовы к нему.

// Реальный объект, доступ к которому нужно контролировать
public interface DocumentService {
    Document getDocument(String id);
    void updateDocument(Document doc);
}

public class RealDocumentService implements DocumentService {
    private Database database;
    
    @Override
    public Document getDocument(String id) {
        // Реальная работа - долгая операция
        return database.fetch(id);
    }
    
    @Override
    public void updateDocument(Document doc) {
        database.save(doc);
    }
}

// Proxy - контролирует доступ
public class DocumentServiceProxy implements DocumentService {
    private RealDocumentService realService;
    private User currentUser;
    private Map<String, Document> cache = new ConcurrentHashMap<>();
    
    public DocumentServiceProxy(RealDocumentService realService, User currentUser) {
        this.realService = realService;
        this.currentUser = currentUser;
    }
    
    @Override
    public Document getDocument(String id) {
        // Контроль доступа - проверка прав
        if (!hasAccessPermission(id)) {
            throw new AccessDeniedException("No access to document " + id);
        }
        
        // Кэширование - оптимизация
        if (cache.containsKey(id)) {
            return cache.get(id);
        }
        
        // Логирование - отслеживание доступа
        logAccess(id);
        
        // Делегирование реальному объекту
        Document doc = realService.getDocument(id);
        
        // Кэширование результата
        cache.put(id, doc);
        
        return doc;
    }
    
    @Override
    public void updateDocument(Document doc) {
        // Контроль доступа - только владелец может обновлять
        if (!isOwner(doc)) {
            throw new AccessDeniedException("Only owner can update");
        }
        
        // Логирование
        logModification(doc);
        
        // Делегирование реальному объекту
        realService.updateDocument(doc);
        
        // Инвалидация кэша
        cache.remove(doc.getId());
    }
    
    private boolean hasAccessPermission(String docId) {
        // Проверка прав доступа
        return true;
    }
    
    private boolean isOwner(Document doc) {
        return doc.getOwnerId().equals(currentUser.getId());
    }
    
    private void logAccess(String docId) {
        System.out.println("User " + currentUser.getId() + " accessed " + docId);
    }
    
    private void logModification(Document doc) {
        System.out.println("User " + currentUser.getId() + " modified " + doc.getId());
    }
}

// Использование
RealDocumentService realService = new RealDocumentService();
DocumentService proxy = new DocumentServiceProxy(realService, currentUser);

// Proxy контролирует все вызовы
Document doc = proxy.getDocument("doc123"); // Проверка прав + кэширование + логирование
proxy.updateDocument(doc); // Проверка прав + логирование

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

АспектAdapterFacadeProxy
ЦельСовместимость интерфейсовУпрощение сложностиКонтроль доступа
СложностьДве несовместимые системыСложная подсистемаОдин объект
ИнтерфейсМожет изменятьсяУпрощённыйТот же
Связь с оригиналом1:1 адаптацияСкрывает множество1:1 делегирование
СозданиеОбёртка для совместимостиАгрегирует подсистемуЗамещает объект
ПримерыLegacy интеграцияREST API контроллерSecurity, Caching, Logging

Реальные примеры из Spring Framework

Adapter:

// Spring использует Adapter для интеграции разных библиотек
// Например: HttpClient -> RestTemplate
public class RestTemplate implements HttpAccessor {
    private HttpClient httpClient; // Старая библиотека
    
    // Адаптирует старый интерфейс к новому
    public ResponseEntity<String> getForEntity(String url) {
        // Конвертирует HttpClient в ResponseEntity
    }
}

Facade:

// Spring контроллер - это Facade для бизнес-логики
@RestController
public class OrderController {
    @PostMapping("/orders")
    public ResponseEntity<?> createOrder(@RequestBody OrderRequest req) {
        // Скрывает сложность: валидация, сохранение, уведомления и т.д.
        orderFacade.createOrder(req);
        return ResponseEntity.ok("Created");
    }
}

Proxy:

// Spring использует Proxy для реализации функций
@Transactional  // Proxy с функциональностью транзакций
public class UserService {
    public void saveUser(User user) {
        // Proxy добавляет управление транзакциями
        // Proxy также может добавлять кэширование (@Cacheable)
    }
}

Ключевые отличия

// ADAPTER - делает несовместимое совместимым
OldInterface old = new OldImplementation();
NewInterface compatible = new AdapterPattern(old); // Адаптация

// FACADE - упрощает сложность
ComplexSubsystem sub = new ComplexSubsystem();
SimpleInterface simple = new FacadePattern(sub); // Упрощение

// PROXY - контролирует доступ
RealObject real = new RealObject();
SameInterface controlled = new ProxyPattern(real); // Контроль

Вывод

  • Adapter нужен когда у вас есть ДВА несовместимых интерфейса, которые нужно объединить
  • Facade нужен когда у вас сложная подсистема, которую нужно упростить для клиентов
  • Proxy нужен когда вы хотите контролировать доступ/поведение реального объекта

Выбор паттерна зависит от ПРОБЛЕМЫ, которую вы решаете, а не от деталей реализации.

В чем разница между шаблонами проектирования Adapter, Facade и Proxy? | PrepBro