Глава 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 основных.
Три категории паттернов
- Creational (Порождающие) — как создавать объекты
- Structural (Структурные) — как организовать структуру
- 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: Подбор паттерна
Для каждой задачи выберите подходящий паттерн:
- Нужно логировать каждый HTTP запрос, добавив timestamp
- Нужно создавать разные типы отчётов (PDF, Excel, HTML)
- Нужно уведомлять пользователей о новых сообщениях
- Нужен только один экземпляр конфигурации приложения
Ответы
- Decorator — добавляем функциональность (логирование)
- Factory — создаём разные типы объектов
- Observer — подписка на события
- 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, мощная комбинация