ЧАСТЬ II: ФУНДАМЕНТ МАСТЕРСТВА

Навык #5: Паттерны проектирования

Глава 8. Навык #5: Паттерны проектирования

"Знание паттернов позволяет направлять AI к правильным решениям одной фразой."


8.1. Направлять AI к правильным решениям

Зачем знать паттерны, если AI знает их

Вопрос: Если AI знает все паттерны, зачем мне их учить?

Ответ: Чтобы направлять AI к правильному решению.

Сравните:

Без знания паттернов:

AI, создай систему уведомлений. Когда пользователь совершает действие,
нужно уведомить другие части системы.

AI может создать что угодно. Возможно, хорошее решение, возможно — плохое.

Со знанием паттернов:

AI, создай систему уведомлений используя паттерн Observer.
События: user_registered, order_placed, payment_received.
Наблюдатели: EmailNotifier, SMSNotifier, AnalyticsLogger.

AI сразу создаст правильную архитектуру с Observer паттерном.

Паттерны — это словарь

Паттерны проектирования — это готовые решения типовых проблем.

Зная паттерны, вы можете сказать AI одним словом, какую архитектуру вы хотите:

  • "Используй Singleton"
  • "Примени Factory"
  • "Сделай через Decorator"

AI сразу поймёт и реализует правильно.


8.2. Основные паттерны (не все 23)

Классические паттерны (Gang of Four)

В книге "Design Patterns" описано 23 паттерна.

Но вам не нужно знать все!

Для эффективной работы с AI достаточно знать 5-7 основных.

Три категории паттернов

  1. Creational (Порождающие) — как создавать объекты
  2. Structural (Структурные) — как организовать структуру
  3. Behavioral (Поведенческие) — как объекты взаимодействуют

8.3. Creational: Singleton, Factory

Singleton — Один экземпляр

Проблема: Нужен только один экземпляр класса (БД подключение, конфиг).

Решение: Singleton

class Database:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._connection = None
        return cls._instance

    def connect(self):
        if self._connection is None:
            self._connection = "Connected to DB"
        return self._connection

# Использование
db1 = Database()
db2 = Database()
print(db1 is db2)  # True — один и тот же объект

С AI:

AI, создай класс Config используя паттерн Singleton.
Должен загружать настройки из config.json при первом обращении.

Factory — Фабрика объектов

Проблема: Нужно создавать разные типы объектов в зависимости от условий.

Решение: Factory

class Button:
    def render(self):
        pass

class WindowsButton(Button):
    def render(self):
        return "Windows style button"

class MacButton(Button):
    def render(self):
        return "Mac style button"

# Factory
class ButtonFactory:
    @staticmethod
    def create_button(os_type):
        if os_type == "windows":
            return WindowsButton()
        elif os_type == "mac":
            return MacButton()
        else:
            raise ValueError(f"Unknown OS: {os_type}")

# Использование
button = ButtonFactory.create_button("windows")
print(button.render())  # Windows style button

С AI:

AI, создай NotificationFactory используя паттерн Factory.
Типы: EmailNotification, SMSNotification, PushNotification.
Метод create_notification(type, recipient, message).

8.4. Structural: Decorator

Decorator — Добавление функциональности

Проблема: Нужно добавить функциональность объекту без изменения его кода.

Решение: Decorator

# Базовый компонент
class Coffee:
    def cost(self):
        return 5

    def description(self):
        return "Coffee"

# Декораторы
class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost() + 1

    def description(self):
        return self._coffee.description() + " + Milk"

class SugarDecorator:
    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost() + 0.5

    def description(self):
        return self._coffee.description() + " + Sugar"

# Использование
coffee = Coffee()
print(f"{coffee.description()}: ${coffee.cost()}")
# Coffee: $5

coffee_with_milk = MilkDecorator(coffee)
print(f"{coffee_with_milk.description()}: ${coffee_with_milk.cost()}")
# Coffee + Milk: $6

coffee_fancy = SugarDecorator(MilkDecorator(Coffee()))
print(f"{coffee_fancy.description()}: ${coffee_fancy.cost()}")
# Coffee + Milk + Sugar: $6.5

В Python — декораторы функций:

def log_execution(func):
    def wrapper(*args, **kwargs):
        print(f"Executing {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Finished {func.__name__}")
        return result
    return wrapper

@log_execution
def calculate(a, b):
    return a + b

calculate(2, 3)
# Executing calculate
# Finished calculate
# → 5

С AI:

AI, создай декораторы для функций:
- @timing — измеряет время выполнения
- @cache — кэширует результаты
- @retry — повторяет при ошибке (max 3 раза)

8.5. Behavioral: Observer, Strategy

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

Проблема: Объекты должны уведомляться о изменениях в другом объекте.

Решение: Observer

# Subject (издатель)
class NewsAgency:
    def __init__(self):
        self._observers = []
        self._news = None

    def attach(self, observer):
        self._observers.append(observer)

    def notify(self):
        for observer in self._observers:
            observer.update(self._news)

    def publish_news(self, news):
        self._news = news
        self.notify()

# Observer (подписчик)
class NewsSubscriber:
    def __init__(self, name):
        self._name = name

    def update(self, news):
        print(f"{self._name} received: {news}")

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

subscriber1 = NewsSubscriber("Alice")
subscriber2 = NewsSubscriber("Bob")

agency.attach(subscriber1)
agency.attach(subscriber2)

agency.publish_news("Breaking: AI writes code!")
# Alice received: Breaking: AI writes code!
# Bob received: Breaking: AI writes code!

С AI:

AI, создай систему событий используя паттерн Observer.
События: OrderPlaced, PaymentReceived, OrderShipped.
Наблюдатели: EmailNotifier, InventoryManager, AnalyticsLogger.

Strategy — Взаимозаменяемые алгоритмы

Проблема: Нужно выбирать алгоритм в runtime.

Решение: Strategy

# Стратегии оплаты
class PaymentStrategy:
    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 CryptoPayment(PaymentStrategy):
    def pay(self, amount):
        return f"Paid ${amount} with Crypto"

# Контекст
class ShoppingCart:
    def __init__(self):
        self._items = []
        self._payment_strategy = None

    def add_item(self, item, price):
        self._items.append((item, price))

    def set_payment_strategy(self, strategy):
        self._payment_strategy = strategy

    def checkout(self):
        total = sum(price for _, price in self._items)
        return self._payment_strategy.pay(total)

# Использование
cart = ShoppingCart()
cart.add_item("Laptop", 1000)
cart.add_item("Mouse", 20)

cart.set_payment_strategy(CreditCardPayment())
print(cart.checkout())  # Paid $1020 with Credit Card

cart.set_payment_strategy(PayPalPayment())
print(cart.checkout())  # Paid $1020 with PayPal

С AI:

AI, создай систему расчёта доставки используя паттерн Strategy.
Стратегии: StandardShipping, ExpressShipping, OvernightShipping.
Каждая стратегия имеет метод calculate(weight, distance).

8.6. Архитектурные: MVC, Repository, DI

MVC — Model-View-Controller

Проблема: Разделить данные, представление и логику.

Решение: MVC

┌─────────┐      ┌────────────┐      ┌──────┐
│  View   │ ←──→ │ Controller │ ←──→ │ Model│
└─────────┘      └────────────┘      └──────┘
   (UI)           (Логика)           (Данные)

Пример:

# Model
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

# View
class UserView:
    def display(self, user):
        print(f"Name: {user.name}, Email: {user.email}")

# Controller
class UserController:
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def update_name(self, new_name):
        self.model.name = new_name

    def show(self):
        self.view.display(self.model)

# Использование
user = User("Alice", "alice@example.com")
view = UserView()
controller = UserController(user, view)

controller.show()
# Name: Alice, Email: alice@example.com

controller.update_name("Alice Smith")
controller.show()
# Name: Alice Smith, Email: alice@example.com

Repository — Абстракция доступа к данным

Проблема: Бизнес-логика не должна знать о деталях БД.

Решение: Repository

# Repository interface
class UserRepository:
    def get_by_id(self, user_id):
        pass

    def save(self, user):
        pass

    def delete(self, user_id):
        pass

# Реализация для PostgreSQL
class PostgreSQLUserRepository(UserRepository):
    def get_by_id(self, user_id):
        # SQL запрос к PostgreSQL
        return User(...)

    def save(self, user):
        # INSERT/UPDATE в PostgreSQL
        pass

# Реализация для MongoDB
class MongoDBUserRepository(UserRepository):
    def get_by_id(self, user_id):
        # Запрос к MongoDB
        return User(...)

    def save(self, user):
        # Сохранение в MongoDB
        pass

# Бизнес-логика не зависит от БД
class UserService:
    def __init__(self, repository: UserRepository):
        self.repo = repository

    def activate_user(self, user_id):
        user = self.repo.get_by_id(user_id)
        user.is_active = True
        self.repo.save(user)

С AI:

AI, создай Repository паттерн для Product.
Методы: get_by_id, get_all, save, delete, find_by_category.
Реализуй PostgreSQLProductRepository и InMemoryProductRepository.

Dependency Injection (DI)

Проблема: Классы жёстко связаны с зависимостями.

Решение: Внедрение зависимостей

# Плохо: жёсткая связь
class UserService:
    def __init__(self):
        self.repo = PostgreSQLUserRepository()  # Жёстко привязаны к PostgreSQL

# Хорошо: DI
class UserService:
    def __init__(self, repository: UserRepository):
        self.repo = repository  # Любая реализация!

# Использование
postgres_repo = PostgreSQLUserRepository()
service = UserService(postgres_repo)

# Легко заменить на другую БД
mongo_repo = MongoDBUserRepository()
service = UserService(mongo_repo)

8.7. Практика: "Используй паттерн X для Y"

Упражнение 1: Подбор паттерна

Для каждой задачи выберите подходящий паттерн:

  1. Нужно логировать каждый HTTP запрос, добавив timestamp
  2. Нужно создавать разные типы отчётов (PDF, Excel, HTML)
  3. Нужно уведомлять пользователей о новых сообщениях
  4. Нужен только один экземпляр конфигурации приложения
Ответы
  1. Decorator — добавляем функциональность (логирование)
  2. Factory — создаём разные типы объектов
  3. Observer — подписка на события
  4. Singleton — один экземпляр

Упражнение 2: Промпт с паттерном

Задача: Система обработки платежей.

Напишите промпт для AI, используя подходящие паттерны.

Пример промпта
AI, создай систему обработки платежей:

1. PaymentProcessor (используй Strategy паттерн)
   - Стратегии: CreditCard, PayPal, Crypto, BankTransfer
   - Метод: process_payment(amount, details)

2. PaymentLogger (используй Decorator)
   - Логирует каждый платёж с timestamp
   - Декорирует PaymentProcessor

3. PaymentFactory (используй Factory паттерн)
   - create_processor(payment_type) → возвращает нужную стратегию

4. PaymentEventPublisher (используй Observer)
   - События: payment_started, payment_completed, payment_failed
   - Наблюдатели: EmailNotifier, AnalyticsTracker, FraudDetector

Реализуй на Python с type hints и docstrings.

8.8. Упражнения

Задание 1: Рефакторинг к паттернам

AI сгенерировал код без паттернов. Определите, какие паттерны применить.

class App:
    def send_notification(self, user, message, type):
        if type == "email":
            # Отправка email
            print(f"Email to {user}: {message}")
        elif type == "sms":
            # Отправка SMS
            print(f"SMS to {user}: {message}")
        elif type == "push":
            # Push уведомление
            print(f"Push to {user}: {message}")

Какой паттерн применить?

Ответ

Strategy или Factory + полиморфизм

class Notifier:
    def send(self, user, message):
        pass

class EmailNotifier(Notifier):
    def send(self, user, message):
        print(f"Email to {user}: {message}")

class SMSNotifier(Notifier):
    def send(self, user, message):
        print(f"SMS to {user}: {message}")

class NotifierFactory:
    @staticmethod
    def create(type):
        if type == "email":
            return EmailNotifier()
        elif type == "sms":
            return SMSNotifier()

Задание 2: Паттерны в реальных проектах

Найдите примеры паттернов в популярных библиотеках:

  • Django (какие паттерны используются?)
  • React (паттерны в компонентах?)
  • Express.js (middleware — какой паттерн?)

Задание 3: AI + Паттерны

Попросите AI создать простое приложение.

Затем попросите рефакторить с применением конкретных паттернов.

Сравните до/после.


Ключевые выводы главы

Паттерны = словарь: Позволяют направлять AI одной фразой

Не нужно знать все 23: Достаточно 5-7 основных

Creational: Singleton (один экземпляр), Factory (создание объектов)

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

Behavioral: Observer (события), Strategy (взаимозаменяемые алгоритмы)

Архитектурные: MVC, Repository, Dependency Injection

С AI: "Используй паттерн X" → AI создаст правильную архитектуру

Проверяйте: AI может применить паттерн неправильно, нужно понимать


Следующая глава: Тестирование — TDD с AI, мощная комбинация