Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Антипаттерны в программировании
Антипаттерны — это повторяющиеся ошибки архитектуры, дизайна и кода. Их нужно избегать.
1. God Object (Божественный объект)
Один класс делает слишком много:
# ❌ Плохо: классический God Object
class User:
def authenticate(self): pass # Аутентификация
def send_email(self): pass # Отправка email
def process_payment(self): pass # Платежи
def generate_report(self): pass # Отчёты
def backup_database(self): pass # Резервное копирование
# 500+ методов!
# ✅ Хорошо: разделение ответственности
class User:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email
class UserAuthenticator:
def authenticate(user, password): pass
class EmailService:
def send(email, subject, body): pass
class PaymentProcessor:
def process(user, amount): pass
2. Primitive Obsession (Примитивная одержимость)
Использование примитивов вместо объектов:
# ❌ Плохо: строки везде
def process_user(name, email, phone, age, role):
if role == "admin":
# Какая роль? "admin", "Admin", "ADMIN"?
pass
# ✅ Хорошо: объект для представления сущности
from enum import Enum
from dataclasses import dataclass
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
@dataclass
class PhoneNumber:
number: str
def __post_init__(self):
if not self.is_valid():
raise ValueError("Invalid phone")
@dataclass
class User:
name: str
email: str
phone: PhoneNumber
age: int
role: UserRole
3. Long Method (Длинный метод)
Метод делает слишком много:
# ❌ Плохо: монолитный метод (200 строк)
def create_order(user_id, items):
# Валидация
if not user_id:
raise ValueError("...")
for item in items:
if not item.get('quantity'):
raise ValueError("...")
# Проверка запасов
for item in items:
stock = get_stock(item['product_id'])
if stock < item['quantity']:
raise Exception("...")
# Расчёт цены
total = 0
for item in items:
price = get_price(item['product_id'])
total += price * item['quantity']
# Применение скидок
user = get_user(user_id)
if user.loyalty_level > 5:
total *= 0.9
# Создание заказа
order = Order(...)
# ... и т.д.
# ✅ Хорошо: разделение на методы
def create_order(user_id, items):
validate_order(user_id, items)
check_inventory(items)
total_price = calculate_price(user_id, items)
apply_discounts(user_id, total_price)
return Order.create(user_id, items, total_price)
4. Duplicate Code (Дублирование кода)
Повторение одного и того же кода в разных местах:
# ❌ Плохо: дублирование
class UserRepository:
def get_active_users(self):
users = []
for user in all_users:
if user.is_active and user.verified:
users.append(user)
return users
class OrderRepository:
def get_active_orders(self):
orders = []
for order in all_orders:
if order.is_active and order.verified:
orders.append(order)
return orders
# ✅ Хорошо: используй функции высокого порядка
def filter_active_and_verified(items):
return [item for item in items if item.is_active and item.verified]
class UserRepository:
def get_active_users(self):
return filter_active_and_verified(all_users)
class OrderRepository:
def get_active_orders(self):
return filter_active_and_verified(all_orders)
5. Shotgun Surgery (Дробовик-хирургия)
Одно изменение требует множества изменений в разных местах:
# ❌ Плохо: расположение сущностей рассеяно
# models.py
class Order:
status: str
# views.py
if order.status == "pending":
send_email()
# utils.py
if order.status == "processing":
update_inventory()
# tasks.py
if order.status == "completed":
send_notification()
# Изменить статусы → нужно трогать везде!
# ✅ Хорошо: централизованное управление состояниями
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PROCESSING = "processing"
COMPLETED = "completed"
# Все статусы в одном месте!
class Order:
def __init__(self):
self.status = OrderStatus.PENDING
def mark_processing(self):
self.status = OrderStatus.PROCESSING
self._on_status_changed()
def _on_status_changed(self):
# Все побочные эффекты в одном месте
if self.status == OrderStatus.PROCESSING:
update_inventory(self)
elif self.status == OrderStatus.COMPLETED:
send_notification(self)
6. Large Class (Большой класс)
Класс с сотнями строк:
# ❌ Плохо: 1000+ строк в одном файле
class Product: # Огромный класс
# Характеристики
# Валидация
# Сохранение в БД
# Расчёты цены
# Управление инвентарём
# Генерация отчётов
# ...
# ✅ Хорошо: разделение ответственности
class Product:
# Только данные товара
id: int
name: str
price: float
class ProductValidator:
@staticmethod
def validate(product): pass
class ProductRepository:
@staticmethod
def save(product): pass
class PricingEngine:
@staticmethod
def calculate(product): pass
7. Feature Envy (Зависть к функциям)
Метод больше интересуется другим классом:
# ❌ Плохо: интерес к чужим данным
class OrderService:
def calculate_total(order):
total = 0
for item in order.items:
price = item.product.price
quantity = item.quantity
tax = item.product.category.tax_rate
total += price * quantity * (1 + tax)
return total
# ✅ Хорошо: каждый класс обрабатывает свои данные
class OrderItem:
def calculate_total(self):
return self.product.price * self.quantity * (1 + self.product.category.tax_rate)
class Order:
def calculate_total(self):
return sum(item.calculate_total() for item in self.items)
# Теперь просто:
total = order.calculate_total()
8. Data Clumps (Клочки данных)
Повторяющиеся группы переменных:
# ❌ Плохо: повторяющиеся параметры везде
def create_order(user_name, user_email, user_phone, ...):
pass
def update_user(user_name, user_email, user_phone):
pass
def send_notification(user_name, user_email, user_phone):
pass
# ✅ Хорошо: группировка в объект
@dataclass
class UserInfo:
name: str
email: str
phone: str
def create_order(user_info: UserInfo):
pass
def update_user(user_info: UserInfo):
pass
def send_notification(user_info: UserInfo):
pass
9. Switch Statements (Длинные switch'и)
Условная логика вместо полиморфизма:
# ❌ Плохо: большой if-else
def calculate_salary(employee_type, base_salary):
if employee_type == "manager":
return base_salary * 1.5 + bonus
elif employee_type == "developer":
return base_salary * 1.2 + bonus
elif employee_type == "intern":
return base_salary
elif employee_type == "contractor":
return base_salary * 0.8
# ✅ Хорошо: полиморфизм
class Employee:
def calculate_salary(self): pass
class Manager(Employee):
def calculate_salary(self):
return self.base_salary * 1.5 + self.bonus
class Developer(Employee):
def calculate_salary(self):
return self.base_salary * 1.2 + self.bonus
class Intern(Employee):
def calculate_salary(self):
return self.base_salary
# Использование:
salary = employee.calculate_salary() # Работает для любого типа!
10. Speculative Generality (Спекулятивная обобщённость)
Код для функциональности, которая может понадобиться:
# ❌ Плохо: оверинжиниринг "на будущее"
class ConfigManager:
def __init__(self):
self.configs = {}
def get_config(self, key, parser=None, validator=None,
cache=True, encrypt=False, ttl=None):
# 10 параметров для функциональности, которая не нужна
pass
# ✅ Хорошо: простое решение для текущей задачи
class ConfigManager:
def __init__(self):
self.configs = {}
def get_config(self, key):
return self.configs.get(key)
def set_config(self, key, value):
self.configs[key] = value
# Добавь функции, когда они понадобятся!
11. Temporary Field (Временные поля)
Поля, которые используются только иногда:
# ❌ Плохо: поле используется редко
class Order:
def __init__(self):
self.items = []
self.temp_calculation = None # Используется только в одном методе!
def apply_discount(self):
self.temp_calculation = self.calculate_base_total()
discount = self.temp_calculation * 0.1
self.temp_calculation = None
return discount
# ✅ Хорошо: используй локальные переменные
class Order:
def apply_discount(self):
base_total = self.calculate_base_total() # Локальная переменная
return base_total * 0.1
12. Message Chains (Цепочки вызовов)
Долгая цепь делегирования:
# ❌ Плохо: долгая цепь вызовов
company.department.manager.user.permissions.can_edit
# Если структура изменится, всё сломается!
# ✅ Хорошо: предоставь метод на более высоком уровне
class Company:
def can_user_edit(self, user_id):
return self.department.manager.can_user_edit(user_id)
# Теперь клиент вызывает просто:
company.can_user_edit(user_id)
13. Middle Man (Посредник)
Класс, который только делегирует другим:
# ❌ Плохо: класс только перенаправляет
class OrderManager:
def __init__(self, repository):
self.repository = repository
def get_order(self, id):
return self.repository.get_order(id)
def save_order(self, order):
return self.repository.save_order(order)
# Просто перенаправление, никакой логики!
# ✅ Хорошо: убери посредника
repository.get_order(id) # Используй напрямую
14. Lazy Class (Ленивый класс)
Класс, который не делает ничего полезного:
# ❌ Плохо: пустой класс
class UserValidator:
@staticmethod
def validate(user):
return True
# ✅ Хорошо: объедини с другим классом или удали
# Если это просто валидация, используй Pydantic:
from pydantic import BaseModel, EmailStr
class UserModel(BaseModel):
name: str
email: EmailStr # Автоматическая валидация!
Чеклист для обнаружения антипаттернов
- Класс > 200 строк? (God Object, Large Class)
- Метод > 20 строк? (Long Method)
- Код повторяется > 2 раз? (Duplicate Code)
- Много параметров функции? (Data Clumps)
- Длинные цепи вызовов? (Message Chains)
- Условная логика вместо полиморфизма? (Switch Statements)
- Код для несуществующих требований? (Speculative Generality)
Решение: Refactoring
# 1. Запусти тесты: убедись, что всё работает
# 2. Малые шаги: один refactoring за раз
# 3. Снова тесты: проверь каждый шаг
# До (плохо)
def process(users):
result = []
for u in users:
if u['active']:
result.append({'id': u['id'], 'name': u['name']})
return result
# Шаг 1: Используй список list comprehension
def process(users):
return [{'id': u['id'], 'name': u['name']} for u in users if u['active']]
# Шаг 2: Используй объект
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
active: bool
def process(users: list[User]) -> list[dict]:
return [{'id': u.id, 'name': u.name} for u in users if u.active]
# Шаг 3: Используй метод класса
def process(users: list[User]) -> list['UserDTO']:
return [UserDTO.from_user(u) for u in users if u.active]
Итоги
Антипаттерны — это признаки плохой архитектуры:
- God Object: Разделяй ответственность
- Long Methods: Выноси подлогику в методы
- Duplicate Code: Используй DRY принцип
- Data Clumps: Группируй данные в объекты
- Switch Statements: Используй полиморфизм
- Speculative Generality: YAGNI (You Aren't Gonna Need It)
Главное правило: Пиши простой, понятный код. Рефакторь постепенно.