Вопрос 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