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

Приведи пример структурного паттерна

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

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

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

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

Структурные паттерны проектирования в Java

Отличный вопрос про паттерны, которые решают проблемы компоновки классов и объектов! Давайте разберёмся на практических примерах.

Что такое структурные паттерны?

Структурные паттерны (Structural Patterns) - это паттерны, которые помогают создавать отношения между объектами для образования более больших структур.

Они решают проблемы:

  • Как скомпоновать классы и объекты в более крупные структуры?
  • Как упростить дизайн, определив простые способы реализации отношений между объектами?

Паттерн Adapter (Адаптер)

Проблема: У тебя есть код, который ожидает интерфейс A, но у тебя есть класс, реализующий интерфейс B. Они несовместимы.

Решение: Создаёшь адаптер, который переводит один интерфейс в другой.

// Старая система - ожидает эту интерфейс
public interface LegacyPaymentSystem {
    void pay(String accountNumber, double amount);
}

// Новая система - использует другой интерфейс
public interface ModernPaymentGateway {
    void processPayment(String customerId, double sum);
}

// Реализация нового интерфейса
public class StripePaymentGateway implements ModernPaymentGateway {
    @Override
    public void processPayment(String customerId, double sum) {
        System.out.println("Processing $" + sum + " for customer: " + customerId);
    }
}

// АДАПТЕР - преобразует ModernPaymentGateway в LegacyPaymentSystem
public class PaymentAdapter implements LegacyPaymentSystem {
    private ModernPaymentGateway modernGateway;

    public PaymentAdapter(ModernPaymentGateway modernGateway) {
        this.modernGateway = modernGateway;
    }

    @Override
    public void pay(String accountNumber, double amount) {
        // Преобразуем старый интерфейс в новый
        modernGateway.processPayment(accountNumber, amount);
    }
}

// Использование
public class BillingService {
    private LegacyPaymentSystem paymentSystem;

    public BillingService(LegacyPaymentSystem paymentSystem) {
        this.paymentSystem = paymentSystem;
    }

    public void billCustomer(String accountNumber, double amount) {
        paymentSystem.pay(accountNumber, amount); // работает со старым интерфейсом
    }
}

public class Main {
    public static void main(String[] args) {
        // Новая система
        ModernPaymentGateway stripe = new StripePaymentGateway();

        // Адаптируем её к старой системе
        LegacyPaymentSystem adapter = new PaymentAdapter(stripe);

        // Старый код работает без изменений
        BillingService billing = new BillingService(adapter);
        billing.billCustomer("ACC123", 99.99);
        // Output: Processing $99.99 for customer: ACC123
    }
}

Паттерн Decorator (Декоратор)

Проблема: Нужно добавить функциональность к объекту динамически, без создания множества подклассов.

Решение: Создаёшь декоратор, который обёртывает объект и добавляет новое поведение.

// Базовый интерфейс - компонент
public interface DataStream {
    String read();
}

// Конкретный компонент - простой поток данных
public class FileDataStream implements DataStream {
    private String content = "Sensitive data from file";

    @Override
    public String read() {
        return content;
    }
}

// ДЕКОРАТОР 1 - добавляет шифрование
public class EncryptionDecorator implements DataStream {
    private DataStream wrappedStream;

    public EncryptionDecorator(DataStream wrappedStream) {
        this.wrappedStream = wrappedStream;
    }

    @Override
    public String read() {
        String data = wrappedStream.read();
        // Добавляем функциональность - шифруем
        return encrypt(data);
    }

    private String encrypt(String data) {
        // Простое шифрование (в реальности - используй Cipher API)
        return "[ENCRYPTED: " + data.hashCode() + "]";
    }
}

// ДЕКОРАТОР 2 - добавляет логирование
public class LoggingDecorator implements DataStream {
    private DataStream wrappedStream;

    public LoggingDecorator(DataStream wrappedStream) {
        this.wrappedStream = wrappedStream;
    }

    @Override
    public String read() {
        System.out.println("[LOG] Reading data...");
        String data = wrappedStream.read();
        System.out.println("[LOG] Data read successfully: " + data);
        return data;
    }
}

// ДЕКОРАТОР 3 - добавляет кэширование
public class CachingDecorator implements DataStream {
    private DataStream wrappedStream;
    private String cache;
    private boolean cached = false;

    public CachingDecorator(DataStream wrappedStream) {
        this.wrappedStream = wrappedStream;
    }

    @Override
    public String read() {
        if (!cached) {
            cache = wrappedStream.read();
            cached = true;
            System.out.println("[CACHE] Data loaded from source");
        } else {
            System.out.println("[CACHE] Returning cached data");
        }
        return cache;
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        // Простой поток данных
        DataStream fileStream = new FileDataStream();
        System.out.println("Raw: " + fileStream.read());
        System.out.println();

        // Добавляем только логирование
        DataStream logged = new LoggingDecorator(fileStream);
        logged.read();
        System.out.println();

        // Комбинируем: логирование + шифрование + кэширование
        DataStream secure = new EncryptionDecorator(
            new LoggingDecorator(
                new CachingDecorator(
                    new FileDataStream()
                )
            )
        );
        System.out.println("\n--- Первый вызов ---");
        secure.read();
        System.out.println("\n--- Второй вызов (из кэша) ---");
        secure.read();
    }
}

// Output:
// Raw: Sensitive data from file
//
// [LOG] Reading data...
// [LOG] Data read successfully: Sensitive data from file
//
// --- Первый вызов ---
// [LOG] Reading data...
// [CACHE] Data loaded from source
// [LOG] Data read successfully: [ENCRYPTED: 1234567890]
// [ENCRYPTED: 1234567890]
//
// --- Второй вызов (из кэша) ---
// [LOG] Reading data...
// [CACHE] Returning cached data
// [LOG] Data read successfully: [ENCRYPTED: 1234567890]
// [ENCRYPTED: 1234567890]

Паттерн Facade (Фасад)

Проблема: У тебя сложная подсистема с множеством классов. Клиенту нужно простой интерфейс.

Решение: Создаёшь фасад, который скрывает сложность.

// Сложная подсистема
public class PaymentProcessor {
    public void validateCardDetails(String cardNumber) {
        System.out.println("Validating card: " + cardNumber);
    }
}

public class FraudDetection {
    public boolean checkFraud(String cardNumber, double amount) {
        System.out.println("Checking fraud for $" + amount);
        return false; // no fraud
    }
}

public class BankingSystem {
    public boolean authorizeTransaction(String accountNumber, double amount) {
        System.out.println("Authorizing transaction for account: " + accountNumber);
        return true;
    }
}

// ФАСАД - упрощает использование подсистемы
public class PaymentFacade {
    private PaymentProcessor paymentProcessor = new PaymentProcessor();
    private FraudDetection fraudDetection = new FraudDetection();
    private BankingSystem bankingSystem = new BankingSystem();

    public boolean processPayment(String cardNumber, String accountNumber, double amount) {
        paymentProcessor.validateCardDetails(cardNumber);

        if (fraudDetection.checkFraud(cardNumber, amount)) {
            System.out.println("Transaction blocked: Fraud detected");
            return false;
        }

        return bankingSystem.authorizeTransaction(accountNumber, amount);
    }
}

// Клиент - использует только фасад
public class ShoppingCart {
    private PaymentFacade paymentFacade = new PaymentFacade();

    public void checkout(String cardNumber, String accountNumber, double total) {
        if (paymentFacade.processPayment(cardNumber, accountNumber, total)) {
            System.out.println("Payment successful!");
        }
    }
}

Сравнение структурных паттернов

ПаттернЗадачаПример
AdapterСовместимость интерфейсовПодключить Stripe к системе, ожидающей иной интерфейс
DecoratorДобавить функциональность динамическиДобавить логирование, шифрование, кэширование
FacadeУпростить сложную подсистемуСкрыть логику платежа за простым интерфейсом
BridgeОтделить абстракцию от реализацииОтделить интерфейс БД от SQL реализации
ProxyКонтролировать доступЛенивая загрузка, проверка прав

Вывод

Структурные паттерны помогают:

  • Упростить дизайн (Facade скрывает сложность)
  • Добавить функциональность (Decorator добавляет поведение)
  • Обеспечить совместимость (Adapter связывает несовместимые интерфейсы)
  • Гибкость и переиспользование (Комбинирование паттернов)

На практике: Decorator используется в Java Collections (Stream обёртки), Adapter в интеграциях, Facade в контроллерах Spring.

Приведи пример структурного паттерна | PrepBro