← Назад к вопросам

Расскажите о шаблонах GRASP. Какие вы применяли на практике?

2.0 Middle🔥 61 комментариев
#Архитектура и паттерны#ООП

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Шаблоны GRASP: принципы объектно-ориентированного дизайна

GRASP (General Responsibility Assignment Software Patterns) — это набор принципов и шаблонов, которые помогают распределять ответственности между классами и объектами в объектно-ориентированном проектировании. В отличие от GoF-шаблонов, которые решают конкретные архитектурные задачи, GRASP фокусируется на фундаментальных вопросах: "какой класс должен выполнять эту задачу?" и "как объекты должны взаимодействовать?". Эти принципы лежат в основе многих известных паттернов и архитектурных подходов.

Основные принципы GRASP

1. Информационный эксперт (Information Expert)

Ответственность должна быть назначена тому классу, который владеет максимумом информации, необходимой для её выполнения. Это краеугольный камень объектно-ориентированного дизайна. На практике я постоянно применяю этот принцип при проектировании бизнес-логики.

class Order
{
    private array $items;
    
    public function calculateTotal(): float
    {
        // Класс Order владеет информацией о товарах
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getPrice() * $item->getQuantity();
        }
        return $total;
    }
}

2. Создатель (Creator)

Класс B должен создавать экземпляры класса A, если:

  • B содержит или агрегирует A
  • B записывает A
  • B активно использует A
  • B имеет данные для инициализации A

На практике это означает, что фабричные методы часто помещаю в те классы, которые естественным образом связаны с создаваемыми объектами.

class OrderFactory
{
    public function createFromCart(ShoppingCart $cart): Order
    {
        // OrderFactory создаёт Order, так как владеет логикой создания
        $order = new Order();
        foreach ($cart->getItems() as $item) {
            $order->addItem($item);
        }
        return $order;
    }
}

3. Контроллер (Controller)

Используйте промежуточный класс для обработки системных событий, не делегируя эту работу классам пользовательского интерфейса. В веб-разработке это напрямую соотносится с контроллерами в MVC.

class OrderController
{
    public function createAction(Request $request): Response
    {
        // Контроллер обрабатывает HTTP-запрос
        $orderData = $request->get('order');
        $order = $this->orderService->createOrder($orderData);
        
        return $this->render('order/created.html.twig', [
            'order' => $order
        ]);
    }
}

4. Слабое зацепление (Low Coupling)

Стремитесь к минимальным зависимостям между классами. Этот принцип я применяю постоянно, особенно при проектировании сервисного слоя.

// ПЛОХО: сильное зацепление
class OrderProcessor
{
    private MySQLDatabase $db;
    private SmtpMailer $mailer;
    private FileLogger $logger;
    
    public function process(Order $order): void
    {
        // Прямые зависимости от конкретных реализаций
    }
}

// ХОРОШО: слабое зацепление через интерфейсы
class OrderProcessor
{
    private OrderRepositoryInterface $repository;
    private NotificationServiceInterface $notifier;
    private LoggerInterface $logger;
    
    public function process(Order $order): void
    {
        // Зависимости от абстракций
    }
}

5. Высокое сцепление (High Cohesion)

Класс должен иметь чётко определённую, узкую ответственность. В практике это означает разбиение "жирных" сервисов на специализированные классы.

6. Полиморфизм (Polymorphism)

Используйте полиморфизм для обработки альтернативных вариантов поведения на основе типа. Широко применяю при реализации стратегий и обработчиков.

interface PaymentMethod
{
    public function pay(float $amount): bool;
}

class CreditCardPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Логика оплаты картой
    }
}

class PayPalPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Логика оплаты через PayPal
    }
}

class PaymentProcessor
{
    public function process(PaymentMethod $method, float $amount): bool
    {
        // Единый интерфейс для различных способов оплаты
        return $method->pay($amount);
    }
}

7. Чистая выдумка (Pure Fabrication)

Создавайте искусственные классы для решения технических задач, не связанных с предметной областью. Пример — репозитории, фасады, сервисы.

8. Посредник (Indirection)

Вводите промежуточный объект для ослабления связи между другими компонентами. Реализуется через паттерн Mediator или шины событий.

9. Устойчивость к изменениям (Protected Variations)

Защищайте точки нестабильности, создавая стабильные интерфейсы вокруг них.

Практическое применение в моих проектах

В реальных PHP-проектах я сознательно применяю эти принципы:

1. Разработка модульной архитектуры

  • Использую Информационного эксперта для распределения бизнес-логики
  • Применяю Слабое зацепление через Dependency Injection
  • Создаю Чистые выдумки (сервисы, репозитории) для изоляции инфраструктурного кода

2. Проектирование сервисного слоя

  • Контроллеры обрабатывают HTTP-запросы (Контроллер)
  • Доменные сервисы содержат бизнес-логику (Высокое сцепление)
  • Репозитории инкапсулируют доступ к данным (Информационный эксперт)

3. Реализация плагинных систем

  • Полиморфизм для interchangeable компонентов
  • Устойчивость к изменениям через стабильные интерфейсы

Пример из практики: при разработке системы электронной коммерции я создал отдельные классы PricingCalculator, InventoryManager и ShippingEstimator, каждый с высокой степенью сцепления. Они взаимодействовали через стабильные интерфейсы, что позволило легко заменять логику расчёта доставки без изменения классов заказов.

GRASP-принципы — это не догма, а инструмент для принятия обоснованных решений при проектировании. Они особенно ценны на этапе анализа требований и создания первоначальной архитектуры, помогая создать систему, которую будет легко поддерживать и расширять в долгосрочной перспективе. В PHP-разработке эти принципы естественным образом сочетаются с практиками SOLID и DDD, формируя прочный фундамент для создания качественного backend-кода.