Design Patterns

Шаблоны решения типовых задач

GoF = Gang of Four

Четверо авторов легендарной книги:

  • Erich Gamma
  • Ralph Johnson
  • Richard Helm
  • John Vlissides

Design Patterns (1994) — первая систематизация шаблонов проектирования

Что такое паттерн?

Готовое решение для типовой проблемы

❌ Проблема

Вы пишете одно и то же:

if model_type == 'nn':
    train_nn(data)
elif model_type == 'tree':
    train_tree(data)
# ...

✅ Паттерн: Strategy

Замените условия на объекты

class Trainer(ABC):
    @abstractmethod
    def train(self, data): pass

class NeuralTrainer(Trainer): ...
class TreeTrainer(Trainer): ...

trainer = trainers[config['type']]
trainer.train(data)

1. Strategy

Разные алгоритмы — один интерфейс

❌ Без паттерна

def normalize(data, method):
    if method == 'minmax':
        return (data - min(data)) / (max(data) - min(data))
    elif method == 'zscore':
        return (data - mean(data)) / std(data)

✅ С Strategy

class Normalizer(ABC):
    @abstractmethod
    def apply(self, data): pass

class MinMaxNormalizer(Normalizer): ...
class ZScoreNormalizer(Normalizer): ...

normalizer = MinMaxNormalizer()
normalized = normalizer.apply(data)

💡 Когда использовать?

  • Есть выбор между алгоритмами
  • Хочется легко добавить новый
  • Много условий вроде if/elif

2. Factory

Создание объектов без знания типа

❌ Вручную

model = None
if config['type'] == 'linear':
    model = LinearModel()
elif config['type'] == 'nn':
    model = NeuralModel()

✅ С Factory

class ModelFactory:
    @staticmethod
    def create(config):
        if config['type'] == 'linear':
            return LinearModel()
        elif config['type'] == 'nn':
            return NeuralModel()

model = ModelFactory.create(config)

💡 Улучшение: реестр

registry = {
    'linear': LinearModel,
    'nn': NeuralModel
}

def create_model(name):
    return registry[name]()

3. Observer

Кто-то изменился — другие узнают

❌ Жёсткая связь

def train_model():
    model.fit(data)
    log_training_result()   # жёстко прописано
    send_email_notification()

✅ С Observer

class EventManager:
    def __init__(self):
        self.listeners = []

    def add_listener(self, listener):
        self.listeners.append(listener)

    def notify(self, event):
        for l in self.listeners:
            l.handle(event)

# Кто угодно может подписаться!
event_bus.add_listener(EmailNotifier())
event_bus.add_listener(Logger())

💡 Аналоги

— События в FastAPI
— Callbacks в обучении модели
— Webhooks

4. Decorator

Добавляем поведение без изменения класса

❌ Модификация

def predict(data):
    print("Start prediction...")  # логгирование
    result = model.predict(data)
    print("Done")                 # логгирование
    return result

✅ С Decorator

@log_execution
def predict(data):
    return model.predict(data)

# или вручную
logged_predict = log_decorator(predict)

💡 Где видели?

@cache, @retry
— Middleware в FastAPI
— Декораторы в Python

5. Adapter

Старое + новое = дружба

❌ Не совместимо

# Новый API требует JSON
result = new_service.process({"data": old_format})  # ошибка!

✅ С Adapter

class OldToNewAdapter:
    def __init__(self, old_service):
        self.old_service = old_service

    def process(self, new_data):
        old_data = self.convert(new_data)
        return self.old_service.run(old_data)

adapter = OldToNewAdapter(legacy_model)
adapter.process(json_input)

💡 Примеры

— API Gateway
— ORM (SQL ↔ объект)
— Форматы данных: CSV → Parquet

🎯 Итог: 5 ключевых паттернов

  • Strategy — выбор алгоритма
  • Factory — создание объектов
  • Observer — подписки на события
  • Decorator — добавление поведения
  • Adapter — совместимость старого и нового
"Не применяйте паттерны ради паттернов.
Применяйте их, когда чувствуете боль."

🏗️ PoSA: Architectural Patterns

Шаблоны архитектуры систем

1. Layered Architecture

Слои: UI → Use Cases → Domain → Data

# Пример структуры
  app/
    ├── presentation/   # API, FastAPI
    ├── application/    # Use cases
    ├── domain/         # Модели, бизнес-логика
    └── infrastructure/ # База, HTTP-клиенты

🔹 Часто используется в Django, Flask, FastAPI

2. MVC (Model-View-Controller)

Разделяем данные, отображение и логику

# Model
  class User:
      def save(self): ...
  
  # View
  def render_profile(user):
      return f"

{user.name}

" # Controller @app.route('/profile') def profile(): user = User.get(1) return render_profile(user)

🔹 Используется в вебе (Django, Rails)

3. Pipes and Filters

Цепочка обработки: данные → фильтр → фильтр → результат

def clean_data(data):
      return [x for x in data if x is not None]
  
  def normalize(data):
      total = sum(data)
      return [x / total for x in data]
  
  def encode_labels(data):
      return [1 if x > 0.5 else 0 for x in data]
  
  # Цепочка
  result = encode_labels(normalize(clean_data(raw_data)))

🔹 Как пайплайн обучения или ETL

4. Broker

Посредник между компонентами (например, Message Broker)

# Производитель
  queue.send("train_model", config)
  
  # Потребитель
  while msg := queue.receive():
      if msg.topic == "train_model":
          train(msg.data)

🔹 Kafka, RabbitMQ, Redis

5. Client-Server

Один сервер обслуживает множество клиентов

# Сервер (FastAPI)
  @app.post("/predict")
  def predict(data):
      return model.predict(data)
  
  # Клиент
  requests.post("http://server/predict", json=input_data)

🔹 Основа всех веб-API

🎯 PoSA vs GoF

Критерий GoF PoSA
Уровень Классы и объекты Система / Архитектура
Примеры Strategy, Observer MVC, Layers, Broker
Где применяется Внутри сервиса Между сервисами

💡 GoF решает "как написать класс"
PoSA решает "как построить систему"