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

На какие критерии смотришь при выборе шаблона проектирования?

2.0 Middle🔥 221 комментариев
#Архитектура и паттерны

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

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

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

Критерии выбора шаблонов проектирования

При выборе паттерна я ориентируюсь на несколько ключевых критериев, которые помогают найти оптимальное решение для конкретной задачи.

1. Проблема, которую мы решаем

Первое — четко определить проблему. Не все паттерны подходят для всех ситуаций:

  • Создание объектов с разными конфигурациями → Builder, Factory
  • Единственный экземпляр → Singleton
  • Замена алгоритма в runtime → Strategy
  • Обработка иерархии объектов → Composite
  • Уменьшение связанности → Observer, Mediator
# Пример: когда Factory имеет смысл
class DataSourceFactory:
    @staticmethod
    def create(db_type: str) -> DatabaseConnection:
        if db_type == "postgres":
            return PostgresConnection()
        elif db_type == "mysql":
            return MysqlConnection()
        raise ValueError(f"Unknown db type: {db_type}")

2. Сложность и overhead

Шаблон должен не усложнить, а упростить код:

  • Паттерн имеет overhead — в нём больше классов и абстракций
  • Если задача проста, паттерн может быть лишним
  • Правило KISS: используй паттерн, только если без него код станет нечитаемым
# ❌ Оверинжиниринг: Builder для простого объекта
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

# Зачем? Просто новый User()

# ✅ Builder имеет смысл для сложного объекта
class DatabaseConfig:
    def __init__(self, host, port, user, password, ssl, pool_size, timeout, ...):
        pass  # 8+ параметров → Builder хорош

3. Гибкость и расширяемость

Смотрю на вероятность изменений:

  • Часто меняется логика? → Strategy, Decorator
  • Много вариаций типов? → Factory, Builder
  • Часто нужны новые функции без изменения класса? → Decorator, Chain of Responsibility
# Пример: много способов сохранить файл
class FileLogger:
    def __init__(self, transport):
        self.transport = transport  # Strategy!
    
    def log(self, message):
        self.transport.write(message)

class FileTransport:
    def write(self, data): pass

class S3Transport(FileTransport):
    def write(self, data): ...  # Легко добавить новый способ

4. Тестируемость

Паттерн должен улучшить тестируемость:

  • Dependency Injection → легко подменять зависимости
  • Strategy → легко тестировать разные алгоритмы
  • Factory → тесты не зависят от конкретной реализации
# ✅ Хорошо тестируется через DI
class UserService:
    def __init__(self, repository: UserRepository):  # Зависимость внутрь
        self.repository = repository
    
    def get_user(self, user_id):
        return self.repository.find(user_id)

# В тестах подменяем
class MockUserRepository(UserRepository):
    def find(self, user_id):
        return User(name="Test")

5. Производительность

Некоторые паттерны имеют пфх-издержки:

  • Proxy/Decorator → дополнительные вызовы
  • Observer → много callback'ов при оповещении
  • Singleton → глобальное состояние (сложнее отлаживать)
# ❌ Singleton усложняет отладку и тесты
class Config:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# ✅ Лучше: передать зависимость
def create_app(config: Config):
    app = App(config)
    return app

6. Знакомство team с паттерном

Не забываю про knowledge tax:

  • Если team не знает паттерн → нужна документация
  • Простой код часто лучше умного паттерна
  • Используй паттерны, которые знают все (Singleton, Factory, Strategy)
  • Сложные паттерны → только если really есть необходимость

Примеры решений в проекте

Вот как я выбирал паттерны в реальном коде:

# 1. Strategy для выбора алгоритма валидации
class Validator:
    def __init__(self, strategy: ValidationStrategy):
        self.strategy = strategy
    
    def validate(self, data):
        return self.strategy.validate(data)

# 2. Factory для создания разных типов кэша
class CacheFactory:
    @staticmethod
    def create(cache_type: str):
        if cache_type == "redis":
            return RedisCache()
        elif cache_type == "memory":
            return MemoryCache()

# 3. Decorator для логирования (не усложняет, добавляет функцию)
class LoggingDecorator:
    def __init__(self, service):
        self.service = service
    
    def execute(self, *args, **kwargs):
        print(f"Executing...")
        result = self.service.execute(*args, **kwargs)
        print(f"Result: {result}")
        return result

Итого: Мой чеклист

  1. Есть ли реальная проблема? → Если нет, не используй паттерн
  2. Упростит ли он код? → Если усложнит, отказываюсь
  3. Team поймет ли его? → Если нет, документирую
  4. Будет ли проще тестировать? → Если нет, пересмотрю
  5. Есть ли performance impact? → Если да, измеряю
  6. Есть ли альтернатива проще? → Обычно есть!

Паттерны — инструменты, а не цель. Лучший паттерн — тот, который решает задачу с минимальной сложностью.

На какие критерии смотришь при выборе шаблона проектирования? | PrepBro