Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое GRASP
GRASP (General Responsibility Assignment Software Patterns) — это набор принципов и паттернов для распределения ответственности между объектами в объектно-ориентированном дизайне. GRASP помогает написать гибкий и поддерживаемый код.
Девять принципов GRASP
1. Information Expert (Информационный эксперт)
Ответственность должна быть у объекта, у которого есть информация.
# Плохо: Calculator знает о структуре Sale
class Calculator:
def calculate_total(self, sale):
total = 0
for item in sale.items:
total += item.price * item.quantity
return total
# Хорошо: Sale сам знает о своей структуре
class Sale:
def __init__(self):
self.items = []
def calculate_total(self):
return sum(item.price * item.quantity for item in self.items)
sale = Sale()
sale.items.append(Item(price=100, quantity=2))
print(sale.calculate_total()) # 200
2. Creator (Создатель)
Ответственность за создание объекта должна быть у класса, который содержит или использует создаваемый объект.
# Плохо: Order создаёт User
class Order:
def __init__(self):
self.user = User() # Странно
# Хорошо: OrderService создаёт Order
class OrderService:
def create_order(self, user_id):
order = Order(user_id=user_id)
return order
class Order:
def __init__(self, user_id):
self.user_id = user_id
3. Controller (Контроллер)
Ответственность за координацию системы должна быть у объекта, который координирует.
# API endpoint — Controller
@router.post("/orders")
async def create_order_controller(order_data: OrderCreate):
# Контроллер координирует
service = OrderService()
order = service.create(order_data)
return {"id": order.id}
# Бизнес-логика — Service
class OrderService:
def create(self, order_data):
order = Order(data=order_data)
# Бизнес-логика
return order
4. Low Coupling (Низкая связанность)
Минимизируй зависимости между классами.
# Плохо: PaymentProcessor зависит от конкретного Payment
class PaymentProcessor:
def process(self, payment: StripePayment):
return payment.charge()
# Хорошо: используй интерфейс
class PaymentProcessor:
def process(self, payment: PaymentProvider):
return payment.charge()
# Можешь использовать любого провайдера
class StripePayment(PaymentProvider):
def charge(self):
pass
class PayPalPayment(PaymentProvider):
def charge(self):
pass
5. High Cohesion (Высокая связность)
Одноответственность класса. Все методы класса работают на одну цель.
# Плохо: User отвечает за слишком многое
class User:
def send_email(self):
pass
def log_activity(self):
pass
def calculate_discount(self):
pass
def process_payment(self):
pass
# Хорошо: разделение ответственности
class User:
def __init__(self, id, email):
self.id = id
self.email = email
class EmailService:
def send_email(self, user):
pass
class ActivityLogger:
def log(self, user, activity):
pass
class DiscountCalculator:
def calculate(self, user):
pass
class PaymentProcessor:
def process(self, user, amount):
pass
6. Polymorphism (Полиморфизм)
Используй полиморфизм вместо условной логики.
# Плохо: много if/else
class PaymentHandler:
def handle(self, payment_type, amount):
if payment_type == 'credit_card':
# обработка кредитки
pass
elif payment_type == 'paypal':
# обработка PayPal
pass
elif payment_type == 'bank_transfer':
# обработка банка
pass
# Хорошо: полиморфизм
class PaymentHandler(ABC):
@abstractmethod
def handle(self, amount):
pass
class CreditCardHandler(PaymentHandler):
def handle(self, amount):
print(f"Processing credit card: {amount}")
class PayPalHandler(PaymentHandler):
def handle(self, amount):
print(f"Processing PayPal: {amount}")
class BankTransferHandler(PaymentHandler):
def handle(self, amount):
print(f"Processing bank transfer: {amount}")
# Использование
handlers = {
'credit_card': CreditCardHandler(),
'paypal': PayPalHandler(),
'bank': BankTransferHandler()
}
handler = handlers[payment_type]
handler.handle(amount) # Работает для любого типа
7. Pure Fabrication (Чистая выдумка)
Создавай объекты для решения проблемы с низкой связанностью, даже если они не соответствуют доменной модели.
# Пример: класс Service создан не в домене, но нужен для логики
class OrderRepository: # Pure Fabrication — не в домене
def save(self, order):
pass
def find_by_id(self, id):
pass
# Это не физический объект, но нужен для хранения заказов
8. Indirection (Косвенность)
Используй промежуточный объект для разделения связанности.
# Плохо: Service напрямую зависит от DB
class OrderService:
def __init__(self):
self.db = DatabaseConnection()
def create_order(self, data):
self.db.insert('orders', data)
# Хорошо: используй Repository как посредник
class OrderService:
def __init__(self, repository):
self.repository = repository # Инъекция зависимости
def create_order(self, data):
order = Order(data)
self.repository.save(order)
class OrderRepository:
def __init__(self, db):
self.db = db
def save(self, order):
self.db.insert('orders', order.to_dict())
9. Protected Variations (Защита вариаций)
Изолируй то, что может меняться.
# Плохо: коды валют hardcoded везде
class Payment:
def calculate_tax(self, amount):
if currency == 'USD':
return amount * 0.1
elif currency == 'EUR':
return amount * 0.2
# Хорошо: вынеси в конфиг или сервис
class TaxCalculator:
def __init__(self, currency_config):
self.config = currency_config
def calculate_tax(self, amount, currency):
tax_rate = self.config.get_tax_rate(currency)
return amount * tax_rate
config = {
'USD': 0.1,
'EUR': 0.2
}
Практический пример: система управления заказами
# Применяем GRASP принципы
from abc import ABC, abstractmethod
# 1. Information Expert
class Order:
def __init__(self, items):
self.items = items
def calculate_total(self): # Order знает о своей структуре
return sum(item.price * item.quantity for item in self.items)
# 2-3. Creator и Controller
class OrderService:
def __init__(self, repository):
self.repository = repository
def create_order(self, items):
order = Order(items)
self.repository.save(order)
return order
# 4-5. Low Coupling и High Cohesion
class OrderRepository: # Pure Fabrication для работы с БД
def __init__(self, db):
self.db = db # Косвенность
def save(self, order):
self.db.insert('orders', order.to_dict())
# 6. Polymorphism
class PaymentProvider(ABC):
@abstractmethod
def charge(self, amount):
pass
class StripePayment(PaymentProvider):
def charge(self, amount):
print(f"Charging Stripe: {amount}")
class PayPalPayment(PaymentProvider):
def charge(self, amount):
print(f"Charging PayPal: {amount}")
# 9. Protected Variations
class PaymentFactory:
def create_payment(self, provider_type):
providers = {
'stripe': StripePayment(),
'paypal': PayPalPayment()
}
return providers[provider_type]
Как GRASP помогает
- Читаемость: Код понятен, ответственности ясны
- Поддерживаемость: Легко изменять части системы
- Масштабируемость: Новые фичи добавляются просто
- Тестируемость: Легко писать юнит-тесты
- Переиспользуемость: Компоненты независимы
Вывод: GRASP — это не конкретные паттерны, а принципы проектирования. Они помогают распределить ответственность правильно и написать гибкий, поддерживаемый код.