← Назад к вопросам
На какие критерии смотришь при выборе шаблона проектирования?
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
Итого: Мой чеклист
- Есть ли реальная проблема? → Если нет, не используй паттерн
- Упростит ли он код? → Если усложнит, отказываюсь
- Team поймет ли его? → Если нет, документирую
- Будет ли проще тестировать? → Если нет, пересмотрю
- Есть ли performance impact? → Если да, измеряю
- Есть ли альтернатива проще? → Обычно есть!
Паттерны — инструменты, а не цель. Лучший паттерн — тот, который решает задачу с минимальной сложностью.