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

Зачем нужны паттерны?

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

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

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

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

Паттерны проектирования (Design Patterns)

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

Почему нужны паттерны

1. Реиспользование опыта

Без паттернов: каждый разработчик решает одну и ту же проблему по-своему
С паттернами: используем решение, проверенное тысячами разработчиков

Это как архитектурные стили в зданиях:
- Зачем изобретать новый стиль, если готика уже доказала свою прочность?

2. Общий язык в команде

Без паттернов:
Коллега: "Как здесь работает кэширование?"
Ты: "Ну, там есть словарь, потом проверяем..."

С паттернами:
Коллега: "Здесь используется Singleton?"
Ты: "Да, для управления подключением к БД"
(Коллега сразу понимает архитектуру)

3. Качество и надежность кода

Паттерны прошли тестирование временем и в боевых условиях
Они учитывают edge cases, которые ты можешь упустить
Использование паттернов снижает количество багов

Основные категории паттернов

Порождающие (Creational) — как создавать объекты

Singleton: один экземпляр на все приложение

class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connect()
        return cls._instance
    
    def connect(self):
        print('Connecting to database...')

# Будет только одно соединение
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True — один и тот же объект

Factory: создание объектов через фабрику

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        return 'Woof'

class Cat(Animal):
    def make_sound(self):
        return 'Meow'

class AnimalFactory:
    @staticmethod
    def create_animal(animal_type):
        if animal_type == 'dog':
            return Dog()
        elif animal_type == 'cat':
            return Cat()
        raise ValueError(f'Unknown animal: {animal_type}')

# Вместо: animal = Dog()
# Используем фабрику
animal = AnimalFactory.create_animal('dog')
print(animal.make_sound())  # Woof

Builder: создание сложных объектов пошагово

class SQLQuery:
    def __init__(self):
        self.select = []
        self.where = []
        self.order_by = []
    
    def add_select(self, *columns):
        self.select.extend(columns)
        return self
    
    def add_where(self, condition):
        self.where.append(condition)
        return self
    
    def add_order_by(self, column):
        self.order_by.append(column)
        return self
    
    def build(self):
        query = f"SELECT {', '.join(self.select)}"
        if self.where:
            query += f" WHERE {' AND '.join(self.where)}"
        if self.order_by:
            query += f" ORDER BY {', '.join(self.order_by)}"
        return query

# Построение запроса пошагово
query = SQLQuery()\
    .add_select('id', 'name', 'email')\
    .add_where('age > 18')\
    .add_where('status = active')\
    .add_order_by('name')\
    .build()

print(query)
# SELECT id, name, email WHERE age > 18 AND status = active ORDER BY name

Структурные (Structural) — как организовать отношения между объектами

Adapter: преобразование несовместимого интерфейса

class OldWeatherAPI:
    def get_temp(self):
        return {'celsius': 25}  # Возвращает Цельсии

class NewWeatherAPI:
    def get_temperature(self):
        return 77  # Возвращает Фаренгейты

class WeatherAdapter:
    def __init__(self, old_api):
        self.api = old_api
    
    def get_temperature(self):
        celsius = self.api.get_temp()['celsius']
        fahrenheit = (celsius * 9/5) + 32
        return fahrenheit

# Адаптируем старый API под новый интерфейс
adapted = WeatherAdapter(OldWeatherAPI())
print(adapted.get_temperature())  # 77

Decorator: добавление функциональности к объекту

class Coffee:
    def cost(self):
        return 100
    
    def description(self):
        return 'Coffee'

class MilkDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    
    def cost(self):
        return self.coffee.cost() + 50
    
    def description(self):
        return self.coffee.description() + ' with Milk'

class SugarDecorator:
    def __init__(self, coffee):
        self.coffee = coffee
    
    def cost(self):
        return self.coffee.cost() + 20
    
    def description(self):
        return self.coffee.description() + ' with Sugar'

# Комбинируем декораторы
coffee = Coffee()
coffee = MilkDecorator(coffee)
coffee = SugarDecorator(coffee)

print(coffee.description())  # Coffee with Milk with Sugar
print(coffee.cost())         # 170

Facade: упрощение сложной системы

class OrderSystem:
    def check_inventory(self, item):
        return True
    
    def process_payment(self, amount):
        return True
    
    def create_shipment(self, address):
        return True

class OrderFacade:
    def __init__(self):
        self.system = OrderSystem()
    
    def place_order(self, item, amount, address):
        # Скрываем сложность системы
        if not self.system.check_inventory(item):
            return False
        if not self.system.process_payment(amount):
            return False
        return self.system.create_shipment(address)

# Клиент использует простой интерфейс
facade = OrderFacade()
facade.place_order('book', 299, 'Moscow')

Поведенческие (Behavioral) — как организовать взаимодействие между объектами

Observer: подписка на события

class EventEmitter:
    def __init__(self):
        self.listeners = {}
    
    def on(self, event, callback):
        if event not in self.listeners:
            self.listeners[event] = []
        self.listeners[event].append(callback)
    
    def emit(self, event, data):
        if event in self.listeners:
            for callback in self.listeners[event]:
                callback(data)

# Использование
emitter = EventEmitter()

def on_user_registered(user):
    print(f'Email sent to {user}')

def on_user_logged(user):
    print(f'Analytics logged for {user}')

emitter.on('user_registered', on_user_registered)
emitter.on('user_registered', on_user_logged)

emitter.emit('user_registered', 'alice@example.com')
# Email sent to alice@example.com
# Analytics logged for alice@example.com

Strategy: выбор алгоритма в runtime

from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount):
        return f'Paid {amount} with credit card'

class PayPalPayment(PaymentStrategy):
    def pay(self, amount):
        return f'Paid {amount} with PayPal'

class CryptoCurrencyPayment(PaymentStrategy):
    def pay(self, amount):
        return f'Paid {amount} with crypto'

class ShoppingCart:
    def __init__(self, payment_strategy: PaymentStrategy):
        self.strategy = payment_strategy
    
    def checkout(self, amount):
        return self.strategy.pay(amount)

# Выбираем стратегию в runtime
cart = ShoppingCart(CreditCardPayment())
print(cart.checkout(100))

cart = ShoppingCart(PayPalPayment())
print(cart.checkout(100))

Chain of Responsibility: передача запроса по цепи

class Handler:
    def __init__(self):
        self.next_handler = None
    
    def set_next(self, handler):
        self.next_handler = handler
        return handler
    
    def handle(self, request):
        if self.next_handler:
            return self.next_handler.handle(request)
        return None

class LoggingHandler(Handler):
    def handle(self, request):
        print(f'Logging: {request}')
        return super().handle(request)

class ValidationHandler(Handler):
    def handle(self, request):
        if not request:
            return 'Invalid request'
        print(f'Validating: {request}')
        return super().handle(request)

class ProcessingHandler(Handler):
    def handle(self, request):
        print(f'Processing: {request}')
        return 'Done'

# Выстраиваем цепь
logger = LoggingHandler()
validator = ValidationHandler()
processor = ProcessingHandler()

logger.set_next(validator).set_next(processor)

logger.handle('user_registration')
# Logging: user_registration
# Validating: user_registration
# Processing: user_registration

Когда использовать паттерны

✅ Используй когда:

# 1. Нужно управлять только одним экземпляром
class DatabaseConnection:  # Singleton
    pass

# 2. Нужна гибкость в создании объектов
class DataSourceFactory:  # Factory
    pass

# 3. Нужно обработать несколько вариантов
if user_type == 'premium':
    strategy = PremiumStrategy()
else:
    strategy = BasicStrategy()  # Strategy

# 4. Нужно расширить функциональность
user = User()  # Decorator
user = LoggingDecorator(user)

❌ Не используй когда:

# 1. Проблема простая
# ❌ Не нужно Factory для простого создания
from helpers import create_user

# 2. Добавляет complexity без пользы
# ❌ Паттерн усложняет код

# 3. Это YAGNI (You Ain't Gonna Need It)
# ❌ Добавляешь паттерн "на будущее"

Итоговое правило

Паттерны нужны чтобы:

  • Решать типичные проблемы проверенными методами
  • Делать код более понятным и поддерживаемым
  • Облегчать коммуникацию в команде
  • Снижать вероятность ошибок

Но помни:

  • Паттерн — это средство, не цель
  • Не применяй паттерн просто потому что он красивый
  • Простой код лучше, чем сложный паттерн
  • Изучай паттерны, но не переиспользуй их
Зачем нужны паттерны? | PrepBro