Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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
- Начните с Information Expert — определите, какой класс имеет данные
- Применяйте Creator для создания объектов
- Используйте Controller для обработки системных событий
- Минимизируйте связанность через Low Coupling и Indirection
- Поддерживайте когезию — каждый класс должен иметь одну ответственность
- Используйте Polymorphism вместо условной логики
- Защищайте от вариаций через интерфейсы
Отличие GRASP от SOLID
GRASP — принципы распределения ответственности SOLID — принципы качества кода
GRASP часто предшествует SOLID при проектировании системы.
Понимание GRASP критично для написания чистого, поддерживаемого и масштабируемого Java кода.