Питання 2 · Розділ 2

Які категорії патернів існують?

Патерни поділяються на три основні категорії залежно від того, що вони допомагають робити:

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Патерни поділяються на три основні категорії залежно від того, що вони допомагають робити:

1. Породжуючі (Creational) — приховують ЩО створюється, КОЛИ і ЯК. Код не повинен залежати від конкретних класів через new.

  • Як Builder — покрокове створення складного об’єкта
  • Як Singleton — гарантія одного екземпляра
  • Як Factory — створення об’єктів через фабрику

2. Структурні (Structural) — допомагають з’єднувати класи

  • Як Adapter — робить сумісними різні інтерфейси
  • Як Decorator — додає функціональність обгорткою
  • Як Proxy — замінює об’єкт сурогатом

3. Поведінкові (Behavioral) — вирішують: хто викликає кого, коли і за яких умов.

  • Як Strategy — заміна алгоритмів
  • Як Observer — підписка на події
  • Як Iterator — перебір елементів

Проста аналогія: Будуємо будинок

  • Породжуючі — як створити цеглу, вікна, двері
  • Структурні — як з’єднати їх разом
  • Поведінкові — як люди користуватимуться будинком

Коли НЕ думати в категоріях категорій

Для простих CRUD-додатків категорії патернів надмірні. Використовуйте патерн тільки коли він вирішує конкретну проблему.


🟡 Middle Level

1. Породжуючі патерни (Creational)

Фокус: Інкапсуляція процесу створення об’єктів

Навіщо: Відокремлення системи від конкретних реалізацій

// ❌ Без Factory — жорсткий зв'язок
User user = new User();
Order order = new Order();

// ✅ З Factory — гнучкість
User user = userFactory.create();
Order order = orderFactory.create();

Ключові патерни:

  • Singleton — один екземпляр
  • Factory Method — створення через метод у підкласі
  • Abstract Factory — створення сімейств об’єктів
  • Builder — покрокове створення
  • Prototype — копіювання об’єкта

У Modern Java:

// Статичні фабричні методи в інтерфейсах
List<String> list = List.of("a", "b", "c");  // Замість new ArrayList()
Optional<String> opt = Optional.of(value);   // Factory Method

2. Структурні патерни (Structural)

Фокус: Компонування класів у більші структури

Навіщо: Вирішення проблем несумісності без наслідування

// Decorator — додавання функціональності
InputStream input = new BufferedInputStream(
                        new FileInputStream("file.txt")
                    );

// Adapter — сумісність інтерфейсів
List<String> list = Collections.enumeration(arrayList);

Ключові патерни:

  • Adapter — поєднання несумісних інтерфейсів
  • Decorator — додавання обов’язків у runtime
  • Proxy — сурогат для контролю доступу
  • Facade — спрощений інтерфейс до підсистеми
  • Composite — деревовидна структура

У Spring:

// Proxy — основа всієї "магії" Spring
@Transactional      // Proxy навколо біна
@Cacheable         // Proxy з кешуванням
@Async             // Proxy для асинхронності

3. Поведінкові патерни (Behavioral)

Фокус: Алгоритми та розподіл обов’язків

Навіщо: Керування динамікою передачі сигналів

// Strategy — заміна алгоритмів
Comparator<String> byLength = (a, b) -> a.length() - b.length();
list.sort(byLength);

// Observer — підписка на події
button.addActionListener(e -> System.out.println("Clicked!"));

Ключові патерни:

  • Strategy — сімейство алгоритмів
  • Observer — підписка на зміни
  • Chain of Responsibility — ланцюжок обробників
  • State — поведінка залежить від стану
  • Template Method — скелет алгоритму

4. Розширена класифікація

Concurrency Patterns:

// Read-Write Lock
ReadWriteLock rwLock = new ReentrantReadWriteLock();

// Producer-Consumer
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
ExecutorService executor = Executors.newFixedThreadPool(10);

Architectural Patterns:

  • MVC — Model-View-Controller
  • Hexagonal Architecture — порти і адаптери
  • CQRS — розділення читання/запису

Cloud-Native Patterns:

  • Circuit Breaker — захист від каскадних збоїв
  • Saga — розподілені транзакції
  • Retry — повторні спроби
  • Bulkhead — ізоляція ресурсів

🔴 Senior Level

Архітектурний сенс категорій

Категорія = рівень керування складністю:

Категорія Яку проблему вирішує Code Smell
Creational Жорсткий зв’язок при створенні Багато new у бізнес-коді
Structural Складність зв’язків та ієрархій Вибух підкласів, несумісність
Behavioral Логіка взаємодії Величезні switch/if-else

Creational: Dependency Inversion на практиці

DIP (Dependency Inversion Principle) — принцип інверсії залежностей: залеж від абстракцій, не від деталей. OCP (Open/Closed Principle) — принцип відкритості/закритості: відкритий для розширення, закритий для модифікації.

Якщо у бізнес-коді багато `new` → порушення DIP

Рішення:
  1. Simple Factory (статичний метод)
  2. Factory Method (наслідування)
  3. Abstract Factory (сімейства)
  4. DI Container (Spring)

Еволюція:

// Рівень 1: Simple Factory
public static UserService create() { return new UserServiceImpl(); }

// Рівень 2: Factory Method
public abstract class Factory { abstract UserService create(); }

// Рівень 3: Abstract Factory
public interface AppFactory {
    UserService createUser();
    OrderService createOrder();
    PaymentService createPayment();
}

// Рівень 4: DI Container
@Component
public class UserService { }  // Spring сам створить

Structural: Композиція vs Наслідування

Decorator замість наслідування:

// ❌ Наслідування → вибух класів
class BufferedFileInputStream extends FileInputStream { }
class EncryptedFileInputStream extends FileInputStream { }
class BufferedEncryptedFileInputStream extends FileInputStream { }
// → 2^n класів для n функцій!

// ✅ Decorator → композиція
new BufferedInputStream(
  new CipherInputStream(
    new FileInputStream("file.txt")
  )
)
// → n класів для n функцій

Behavioral: Поліморфізм замість switch

// ❌ Процедурний підхід
switch (paymentType) {
    case CREDIT_CARD: processCreditCard(); break;
    case PAYPAL: processPayPal(); break;
    case CRYPTO: processCrypto(); break;
}

// ✅ ООП підхід (Strategy)
interface PaymentStrategy { void process(); }
class CreditCardPayment implements PaymentStrategy { ... }
class PayPalPayment implements PaymentStrategy { ... }

paymentStrategy.process();  // Поліморфізм

Modern Java:

// Enum Map Strategy
Map<PaymentType, PaymentStrategy> strategies = Map.of(
    CREDIT_CARD, this::processCreditCard,
    PAYPAL, this::processPayPal,
    CRYPTO, this::processCrypto
);

strategies.get(type).process();

Взаємозв’язок категорій у реальних системах

Spring Context як “Мета-фабрика”:

ApplicationContext (Creational)
  → створює біни
    → огортає у Proxy (Structural)
      → які керують поведінкою (Behavioral)

Типовий ланцюжок:

Abstract Factory (Creational)
  → повертає Proxy (Structural)
    → який огортає бізнес-логіку
      → керовану через Strategy (Behavioral)
        → збирану через Builder (Creational)
          → з Chain of Responsibility (Behavioral) для валідації

Performance Implications

JIT і поліморфізм:

Monomorphic call (1 реалізація)
  → JIT інлайнить код
  → 0 накладних витрат

Bimorphic call (2 реалізації)
  → JIT все ще оптимізує
  → Мінімальні витрати

Megamorphic call (>2 реалізації)
  → Непрямий виклик через vtable
  → Вимірюване просідання по CPU

Оптимізація для Hot-path:

// Замість інтерфейсу → enum
public enum CompressionType {
    GZIP { @Override public byte[] compress(byte[] data) { ... } },
    LZ4 { @Override public byte[] compress(byte[] data) { ... } };
    public abstract byte[] compress(byte[] data);
}

// → JIT краще оптимізує enum switch table

Cloud-Native Patterns Deep Dive

Circuit Breaker:

@CircuitBreaker(name = "backendA", fallbackMethod = "fallback")
public String getData() {
    return restTemplate.getForObject("http://service-a", String.class);
}

Saga Pattern:

Order Service → створити замовлення
  ↓
Payment Service → списати гроші
  ↓
Inventory Service → зарезервувати товар
  ↓ (якщо помилка)
Compensating Transactions → скасувати все

Production Experience

Реальний сценарій: Категоризація допомогла знайти корінь проблеми

  • Система: 200+ класів, складно зрозуміти що де
  • Аналіз за категоріями:
    • Creational: 30 Factory класів (overengineering)
    • Structural: 50 Decorator (потрібно)
    • Behavioral: 80 Strategy (дублювання)
  • Рішення:
    • Замінили Factory на DI
    • Consolidate Strategy → Map
  • Результат: -40% класів, +читабельність

Best Practices

  1. Визначайте категорію при виборі патерну
  2. Creational → боріться з new у бізнес-коді
  3. Structural → композиція > наслідування
  4. Behavioral → поліморфізм > switch
  5. Cloud-Native обов’язкові для мікросервісів
  6. Перевіряйте взаємозв’язки між патернами
  7. Думайте про JIT при виборі реалізації
  8. Modern Java спрощує багато категорій

Резюме для Senior

  • Creational = боротьба з жорстким зв’язком при створенні
  • Structural = керування складністю зв’язків
  • Behavioral = керування логікою взаємодії
  • Категорія = рівень керування архітектурною складністю
  • Взаємозв’язок: патерни різних категорій працюють разом
  • JIT: megamorphic calls впливають на продуктивність
  • Cloud-Native патерни критичні для мікросервісів
  • Spring Context = мега-фабрика всіх категорій

🎯 Шпаргалка для інтерв’ю

Обов’язково знати:

  • Три основні категорії: Creational (створення об’єктів), Structural (композиція класів), Behavioral (взаємодія)
  • Creational приховують процес створення, Structural вирішують проблеми сумісності, Behavioral керують алгоритмами
  • У Modern Java: Factory Method замінюється Supplier, Singleton — DI-контейнером (Spring @Component)
  • Spring @Transactional, @Cacheable, @Async працюють через Proxy (Structural патерн)
  • Cloud-Native патерни: Circuit Breaker, Saga, Retry, Bulkhead — обов’язкові для мікросервісів
  • Категорії допомагають визначати корінь архітектурних проблем і обирати правильне рішення
  • Еволюція: Simple Factory → Factory Method → Abstract Factory → DI Container

Часті уточнювальні запитання:

  • Який патерн замінює switch у бізнес-коді? — Strategy (Behavioral), Map<Enum, Function>
  • Що таке megamorphic call? — Виклик методу з >2 реалізаціями інтерфейсу, JIT не може інлайнити
  • Як Spring керує створенням бінів? — ApplicationContext як “мега-фабрика” всіх категорій патернів
  • Коли категорії патернів надмірні? — У простих CRUD-додатках і прототипах

Червоні прапорці (НЕ говорити):

  • “Мені не потрібні категорії, я пишу код без патернів” — незнання архітектурних основ
  • “Всі патерни застаріли з приходом Spring” — Spring сам використовує патерни під капотом
  • “Creational патерни потрібні тільки в Java” — вони застосовні в будь-якій ООП мові
  • “Структурні патерни — це тільки для GUI” — Adapter, Decorator, Proxy всюди у Spring

Пов’язані теми:

  • [[1. Що таке патерни проектування]] — загальний вступ і рівні патернів
  • [[3. Що таке Singleton]] — Creational патерн
  • [[12. В чому перевага Decorator перед наслідуванням]] — Structural патерн
  • [[10. Коли використовувати Strategy]] — Behavioral патерн
  • [[14. Які типи Proxy існують]] — Structural патерн у Spring