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

Что такое GRASP?

1.8 Middle🔥 111 комментариев
#SOLID и паттерны проектирования

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

GRASP: Общие паттерны распределения ответственности

GRASP (General Responsibility Assignment Software Patterns) — это набор из 9 фундаментальных паттернов для распределения ответственности между объектами при проектировании объектно-ориентированных систем. GRASP помогает создавать более гибкие, поддерживаемые и масштабируемые приложения. Паттерны описаны Craig Larman в его книге «Applying UML and Patterns».

Основная идея GRASP

Отличается от Gang of Four (GoF) паттернов тем, что фокусируется на принципах распределения ответственности, а не на конкретных архитектурных решениях.

9 GRASP паттернов

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

Ответственность: создание объектов

Правило: класс X должен создавать объекты класса Y если:

  • X содержит Y
  • X агрегирует коллекцию Y
  • X записывает Y
  • X использует Y наиболее часто
  • X имеет инициализирующие данные для Y
// Плохо: Order создает LineItem, но LineItem может быть создан в разных местах
public class Order {
    public void addItem(String productId, int quantity) {
        LineItem item = new LineItem(productId, quantity);
        items.add(item);
    }
}

// Хорошо: Factory pattern согласно GRASP Creator
public class Order {
    private LineItemFactory factory;
    
    public void addItem(String productId, int quantity) {
        LineItem item = factory.createLineItem(productId, quantity);
        items.add(item);
    }
}

public class LineItemFactory {
    public LineItem createLineItem(String productId, int quantity) {
        return new LineItem(productId, quantity);
    }
}

2. Information Expert (Эксперт по информации)

Ответственность: объекты должны выполнять операции над данными, которыми они владеют

Правило: назначьте ответственность класс, который имеет информацию, необходимую для её выполнения

// Плохо: OrderProcessor имеет логику расчета цены
public class OrderProcessor {
    public double calculateTotal(Order order) {
        double total = 0;
        for (LineItem item : order.getItems()) {
            total += item.getQuantity() * item.getProduct().getPrice();
        }
        return total;  // Логика разбросана
    }
}

// Хорошо: Order знает, как рассчитать свою сумму
public class Order {
    private List<LineItem> items;
    
    public double calculateTotal() {
        double total = 0;
        for (LineItem item : items) {
            total += item.calculatePrice();
        }
        return total;
    }
}

public class LineItem {
    private Product product;
    private int quantity;
    
    public double calculatePrice() {
        return quantity * product.getPrice();
    }
}

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

Ответственность: обработка системных событий

Правило: создайте класс для обработки системных событий (он же Application Controller)

// GRASP Controller: обрабатывает операции от UI/API
public class OrderController {
    private OrderService orderService;
    private OrderRepository orderRepository;
    
    public Order createOrder(CreateOrderRequest request) {
        // Обработка системного события
        Order order = new Order(request.getCustomerId());
        
        for (OrderItemRequest itemRequest : request.getItems()) {
            order.addItem(
                itemRequest.getProductId(),
                itemRequest.getQuantity()
            );
        }
        
        orderRepository.save(order);
        return order;
    }
}

// REST endpoint использует Controller
@RestController
@RequestMapping("/api/orders")
public class OrderRestController {
    private OrderController orderController;
    
    @PostMapping
    public ResponseEntity<Order> create(@RequestBody CreateOrderRequest request) {
        Order order = orderController.createOrder(request);
        return ResponseEntity.ok(order);
    }
}

4. Low Coupling (Слабая связанность)

Ответственность: минимизировать зависимости

Правило: назначайте ответственность так, чтобы связанность была как можно меньше

// Плохо: высокая связанность
public class OrderProcessor {
    private EmailService emailService;  // прямая зависимость
    private DatabaseConnection dbConn;  // прямая зависимость
    
    public void processOrder(Order order) {
        dbConn.update(order);
        emailService.sendConfirmation(order);  // жестко связано
    }
}

// Хорошо: низкая связанность через интерфейсы
public class OrderProcessor {
    private OrderRepository repository;  // интерфейс
    private NotificationService notificationService;  // интерфейс
    
    public OrderProcessor(OrderRepository repository, 
                          NotificationService notificationService) {
        this.repository = repository;
        this.notificationService = notificationService;
    }
    
    public void processOrder(Order order) {
        repository.save(order);
        notificationService.notifyCustomer(order);
    }
}

public interface OrderRepository {
    void save(Order order);
}

public interface NotificationService {
    void notifyCustomer(Order order);
}

5. High Cohesion (Высокая когезия)

Ответственность: класс должен иметь связанную, сфокусированную функциональность

Правило: назначайте ответственность так, чтобы объекты оставались узкоспециализированными

// Плохо: низкая когезия
public class OrderManager {
    // Управление заказами
    public Order createOrder() { ... }
    public void updateOrder(Order order) { ... }
    
    // Управление платежами
    public void processPayment(Order order) { ... }
    
    // Отправка email
    public void sendEmail(String to, String subject) { ... }
    
    // Логирование
    public void logEvent(String event) { ... }
}

// Хорошо: высокая когезия
public class OrderManager {
    private OrderRepository repository;
    private PaymentProcessor paymentProcessor;
    private NotificationService notificationService;
    
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order(request);
        repository.save(order);
        return order;
    }
}

public class PaymentProcessor {
    public PaymentResult process(Order order, Payment payment) {
        // только платежи
        return processPayment(payment);
    }
}

public class EmailNotificationService implements NotificationService {
    public void notifyCustomer(Order order) {
        // только уведомления
    }
}

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

Ответственность: обработка вариантов поведения через полиморфизм

Правило: используйте наследование и интерфейсы вместо условной логики

// Плохо: условная логика
public class PaymentProcessor {
    public void process(Payment payment) {
        if (payment.getType().equals("CREDIT_CARD")) {
            processCreditCard(payment);
        } else if (payment.getType().equals("PAYPAL")) {
            processPayPal(payment);
        } else if (payment.getType().equals("BANK_TRANSFER")) {
            processBankTransfer(payment);
        }
    }
}

// Хорошо: полиморфизм
public interface PaymentMethod {
    PaymentResult process(Payment payment);
}

public class CreditCardPayment implements PaymentMethod {
    public PaymentResult process(Payment payment) {
        // обработка кредитной карты
    }
}

public class PayPalPayment implements PaymentMethod {
    public PaymentResult process(Payment payment) {
        // обработка PayPal
    }
}

public class BankTransferPayment implements PaymentMethod {
    public PaymentResult process(Payment payment) {
        // обработка банковского перевода
    }
}

public class PaymentProcessor {
    public PaymentResult process(Payment payment) {
        PaymentMethod method = getPaymentMethod(payment);
        return method.process(payment);  // Полиморфизм
    }
}

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

Ответственность: добавить промежуточный объект для снижения связанности

Правило: если два объекта не должны взаимодействовать напрямую, создайте посредника

// Плохо: прямая зависимость
public class Order {
    private Database database;  // Order зависит от Database
    
    public void save() {
        database.insert(this);  // прямое использование БД
    }
}

// Хорошо: посредник (Repository)
public class Order {
    // Order не знает о БД
}

public interface OrderRepository {
    void save(Order order);
}

public class DatabaseOrderRepository implements OrderRepository {
    private Database database;
    
    public void save(Order order) {
        database.insert(order);  // Only repository знает о БД
    }
}

public class OrderService {
    private OrderRepository repository;  // использует посредника
    
    public void createOrder(Order order) {
        repository.save(order);
    }
}

8. Protected Variations (Защита от вариаций)

Ответственность: изолировать код от изменений

Правило: определите точки вариаций и изолируйте их за интерфейсом

// Вариация: способ получения данных
public interface DataProvider {
    Data getData();
}

public class DatabaseDataProvider implements DataProvider {
    public Data getData() {
        // получение из БД
    }
}

public class CacheDataProvider implements DataProvider {
    public Data getData() {
        // получение из кэша
    }
}

public class ReportGenerator {
    private DataProvider dataProvider;  // изолирован от вариаций
    
    public Report generate() {
        Data data = dataProvider.getData();
        return createReport(data);
    }
}

9. Pure Fabrication (Чистый вымысел)

Ответственность: создать искусственный класс для повышения когезии

Правило: если класс нарушает Low Coupling и High Cohesion, создайте вспомогательный класс

// Проблема: Order считает налоги, но это не его ответственность
public class Order {
    public double getTotal() {
        double subtotal = calculateSubtotal();
        double tax = calculateTax(subtotal);  // не основная ответственность
        return subtotal + tax;
    }
}

// Решение: Pure Fabrication (TaxCalculator вымышленный класс)
public class Order {
    private TaxCalculator taxCalculator;
    
    public double getTotal() {
        double subtotal = calculateSubtotal();
        double tax = taxCalculator.calculateTax(subtotal);
        return subtotal + tax;
    }
}

public class TaxCalculator {
    public double calculateTax(double amount) {
        return amount * 0.18;  // Чистая ответственность
    }
}

Пример: E-commerce система с GRASP

// 1. Information Expert: Product знает свою цену
public class Product {
    private String id;
    private String name;
    private double price;
    
    public double getPrice() {
        return price;
    }
}

// 2. Creator: Order создает LineItem
public class Order {
    private List<LineItem> items;
    private OrderStatus status;
    
    public void addItem(Product product, int quantity) {
        LineItem item = new LineItem(product, quantity);  // Creator
        items.add(item);
    }
    
    public double getTotal() {
        return items.stream()
            .mapToDouble(LineItem::getPrice)
            .sum();
    }
}

// 3. Information Expert: LineItem считает свою стоимость
public class LineItem {
    private Product product;
    private int quantity;
    
    public double getPrice() {
        return product.getPrice() * quantity;  // Information Expert
    }
}

// 4. Controller: OrderController обрабатывает системные события
public class OrderController {
    private OrderService orderService;
    
    public Order create(CreateOrderRequest request) {
        Order order = orderService.createOrder(request);
        return order;
    }
}

// 5. Low Coupling: OrderService использует интерфейсы
public class OrderService {
    private OrderRepository orderRepository;  // интерфейс
    private PaymentService paymentService;    // интерфейс
    
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order();
        // ...
        orderRepository.save(order);
        return order;
    }
}

// 6. Polymorphism: разные способы оплаты
public interface PaymentMethod {
    boolean canProcess(Order order);
    PaymentResult process(Order order);
}

public class CreditCardPayment implements PaymentMethod { ... }
public class PayPalPayment implements PaymentMethod { ... }

Лучшие практики применения GRASP

  1. Начните с Information Expert — определите, какой класс имеет данные
  2. Применяйте Creator для создания объектов
  3. Используйте Controller для обработки системных событий
  4. Минимизируйте связанность через Low Coupling и Indirection
  5. Поддерживайте когезию — каждый класс должен иметь одну ответственность
  6. Используйте Polymorphism вместо условной логики
  7. Защищайте от вариаций через интерфейсы

Отличие GRASP от SOLID

GRASP — принципы распределения ответственности SOLID — принципы качества кода

GRASP часто предшествует SOLID при проектировании системы.

Понимание GRASP критично для написания чистого, поддерживаемого и масштабируемого Java кода.