Что такое GRASP-паттерны в объектно-ориентированном проектировании?

Ответ

GRASP (General Responsibility Assignment Software Patterns — Общие паттерны распределения обязанностей в ПО) — это набор принципов и рекомендаций для распределения обязанностей между классами и объектами в объектно-ориентированном проектировании. Их основная цель — создание гибких, понятных, легко поддерживаемых и расширяемых систем.

Основные паттерны GRASP:

  1. Информационный эксперт (Information Expert): Назначать ответственность классу, который владеет всей необходимой информацией для выполнения этой ответственности. Это способствует высокой связности и низкой связанности.
  2. Создатель (Creator): Класс, создающий объекты, должен их содержать, агрегировать или тесно использовать. Это упрощает управление зависимостями и инкапсулирует логику создания.
  3. Контроллер (Controller): Использовать промежуточный класс для обработки системных событий или операций, делегируя их выполнение другим объектам. Это отделяет пользовательский интерфейс от бизнес-логики.
  4. Низкая связанность (Low Coupling): Минимизировать зависимости между классами. Низкая связанность делает систему более устойчивой к изменениям, легче тестируемой и повторно используемой.
  5. Высокая связность (High Cohesion): Группировать связанные обязанности в одном классе. Высокая связность улучшает читаемость, управляемость и повторное использование кода.
  6. Полиморфизм (Polymorphism): Использовать полиморфизм для обработки альтернативных вариантов поведения на основе типа объекта, а не условных операторов. Это упрощает расширение системы новыми типами.
  7. Чистая выдумка (Pure Fabrication): Создавать искусственные классы, не соответствующие доменной модели, для соблюдения принципов высокой связности и низкой связанности, когда другие паттерны не применимы.
  8. Индеректность (Indirection): Добавлять промежуточный объект для развязки двух других элементов, чтобы уменьшить их прямую связанность и повысить гибкость.
  9. Устойчивость к изменениям (Protected Variations): Защищать элементы от изменений других элементов, используя интерфейсы, полиморфизм и индиректность. Это позволяет изолировать изменения.

Пример: Информационный эксперт

Класс Order является информационным экспертом для расчета общей стоимости, так как он владеет списком items и их свойствами.

class Item:
    def __init__(self, price: float, quantity: int):
        self.price = price
        self.quantity = quantity

class Order:
    def __init__(self, items: list[Item]):
        self.items = items

    def calculate_total(self) -> float:
        """Рассчитывает общую стоимость заказа."""
        # Order знает свои items, поэтому логично считать сумму здесь
        return sum(item.price * item.quantity for item in self.items)

# Использование:
item1 = Item(price=10.0, quantity=2)
item2 = Item(price=5.5, quantity=3)
order = Order(items=[item1, item2])
print(f"Общая стоимость заказа: {order.calculate_total():.2f}") # Вывод: 36.50

Применение GRASP-паттернов помогает создавать более гибкие, понятные и легко поддерживаемые объектно-ориентированные системы, улучшая их архитектуру и качество кода.

Ответ 18+ 🔞

Давай я тебе на пальцах, без этих заумных книжек, объясню, что такое GRASP. Представь, ты заходишь в общагу, а там пиздец — кто за что отвечает, непонятно. Один за пиво, другой за чипсы, третий сидит на унитазе и не выходит. Вот GRASP — это как раз правила, чтобы такого бардака в твоём коде не было. Чтоб каждый класс знал свою жопу и не лез в чужую.

Основные принципы, или как не накосячить:

  1. Информационный эксперт (Information Expert): Отдай задачу тому, у кого все данные уже есть. Не заставляй соседа по койке бегать за твоими носками, если они у тебя под подушкой. Кто владеет инфой — тот и паши.
  2. Создатель (Creator): Кто должен плодить объекты? Тот, кто их потом использует, содержит или ими командует. Не заставляй постороннего дядю рожать твоих детей. Логично же?
  3. Контроллер (Controller): Нужен главный по тарелкам, который принимает запросы (типа «принеси пива») и раздаёт указания другим (холодильнику, бутылкозакупщику). Чтобы не было так, что твой интерфейс сам лезет в бизнес-логику, как слон в посудную лавку.
  4. Низкая связанность (Low Coupling): Делай так, чтобы классы мало зависели друг от друга. Идеал — как лебедь, рак да щука. Шутка. На самом деле, чтобы изменение в одном месте не вызывало взрыв нахуй во всей системе.
  5. Высокая связность (High Cohesion): Свали в одну кучу (класс) только то, что реально связано. Не делай из класса «швейцарский нож», который и пиво открывает, и борщ варит, и в танке воюет. Одна ответственность — и все довольны.
  6. Полиморфизм (Polymorphism): Если у тебя куча if-else на проверку типа («если это собака — гавкни, если кошка — мяукни»), то ты делаешь хуйню. Пусть каждый объект сам знает, как ему себя вести. Добавишь хомячка — просто новый класс, а не копипасту условий.
  7. Чистая выдумка (Pure Fabrication): Иногда надо создать класс-пустышку, которого в реальном мире нет, просто чтобы разгрести бардак. Типа «менеджер» или «сервис». Главное — чтобы от этого не стало ещё хуже.
  8. Индиректность (Indirection): Чтобы два буйных класса не подрались напрямую, подставь между ними посредника. Как дипломат на переговорах. Связанность падает, гибкость растёт.
  9. Устойчивость к изменениям (Protected Variations): Защити свои модули от чужих правок. Спрячь хрупкие части за интерфейсами. Если что-то поменяется — твоя система не развалится, как карточный домик.

Пример на живца: Информационный эксперт

Смотри, вот заказ (Order). Кто должен считать его полную стоимость? Тот, у кого уже есть список всех товаров (items), ебать его в сраку! Не какой-то левый калькулятор, который будет выпрашивать данные, а сам заказ. Он и есть тот самый эксперт.

class Item:
    def __init__(self, price: float, quantity: int):
        self.price = price
        self.quantity = quantity

class Order:
    def __init__(self, items: list[Item]):
        self.items = items  # Вся инфа тут!

    def calculate_total(self) -> float:
        """Считает бабки."""
        # Order — информационный эксперт. Он знает свои items, вот пусть и пашет.
        return sum(item.price * item.quantity for item in self.items)

# Проверяем:
item1 = Item(price=10.0, quantity=2)
item2 = Item(price=5.5, quantity=3)
order = Order(items=[item1, item2])
print(f"Итого к оплате: {order.calculate_total():.2f}") # Выведет: 36.50

Вот и вся магия, ёпта. GRASP — это не ракетостроение, а просто здравый смысл, оформленный в паттерны. Применяй их, и твой код перестанет быть свалкой, где всё зависит ото всего, а станет чем-то вменяемым. Ну, почти.