Приведи пример структурного паттерна
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Структурные паттерны проектирования в 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.