프로젝트

일반

사용자정보

GRASP 패턴: Pure Fabrication (순수 가공)

Prof. Jong Min Lee이(가) 약 한달 전에 추가함

GRASP 패턴: Pure Fabrication (순수 가공) - 간단한 설명과 예시

핵심 아이디어: 도메인 객체가 아닌, 특정 책임을 수행하기 위해 인위적으로 만들어진 클래스에게 책임을 할당합니다. 이는 Information Expert나 Creator와 같은 다른 GRASP 패턴을 적용하기 어려울 때, 도메인 객체의 응집도를 높이고 결합도를 낮추기 위해 사용됩니다.

간단한 설명:

때로는 자연스러운 도메인 모델의 클래스 중 어느 하나에게도 특정 책임을 할당하기 어색하거나, 그렇게 할 경우 클래스의 응집도가 낮아지거나 다른 클래스와의 결합도가 높아지는 상황이 발생합니다. 이럴 때 순수 가공 패턴은 해당 책임을 수행하기 위한 별도의 클래스를 만들어 책임을 할당하도록 제안합니다. 이 새롭게 만들어진 클래스는 도메인 모델의 개념을 직접적으로 반영하지는 않지만, 시스템의 설계 품질을 향상시키는 역할을 합니다.

예시:

온라인 쇼핑몰에서 주문 처리 시스템을 생각해 봅시다. 주문(Order) 객체는 주문 아이템 목록, 총 가격 등의 정보를 가지고 있습니다. 주문을 처리하는 과정에는 다음 단계들이 포함될 수 있습니다.

  1. 재고 확인
  2. 결제 처리
  3. 배송 정보 생성
  4. 주문 완료 알림 발송

만약 이 모든 책임을 Order 클래스에 할당한다면, Order 클래스는 너무 많은 책임을 가지게 되어 응집도가 낮아지고, 재고 관리 시스템, 결제 시스템, 배송 시스템 등 다양한 외부 시스템과 직접적으로 결합되어 결합도가 높아질 수 있습니다.

이러한 문제를 해결하기 위해 OrderProcessor라는 순수 가공 클래스를 만들어서 주문 처리 관련 책임을 할당할 수 있습니다.

// 도메인 클래스
class Order {
    private List<OrderItem> items;
    private double totalPrice;

    public Order(List<OrderItem> items) {
        this.items = items;
        this.totalPrice = calculateTotalPrice();
    }

    public List<OrderItem> getItems() {
        return items;
    }

    public double getTotalPrice() {
        return totalPrice;
    }

    private double calculateTotalPrice() {
        // ... 아이템 가격 합산 로직 ...
        return 0.0; // 예시
    }
}

class OrderItem {
    private String productId;
    private int quantity;
    private double price;

    // ... 생성자 및 getter ...
}

// 순수 가공 클래스: 주문 처리 로직을 담당
class OrderProcessor {
    private InventoryService inventoryService;
    private PaymentGateway paymentGateway;
    private ShippingService shippingService;
    private NotificationService notificationService;

    public OrderProcessor(InventoryService inventoryService, PaymentGateway paymentGateway, ShippingService shippingService, NotificationService notificationService) {
        this.inventoryService = inventoryService;
        this.paymentGateway = paymentGateway;
        this.shippingService = shippingService;
        this.notificationService = notificationService;
    }

    public boolean processOrder(Order order, PaymentInfo paymentInfo, ShippingAddress shippingAddress) {
        // 1. 재고 확인
        if (!inventoryService.checkInventory(order.getItems())) {
            return false; // 재고 부족
        }

        // 2. 결제 처리
        if (!paymentGateway.processPayment(order.getTotalPrice(), paymentInfo)) {
            return false; // 결제 실패
        }

        // 3. 배송 정보 생성
        shippingService.createShippingLabel(order.getItems(), shippingAddress);

        // 4. 주문 완료 알림 발송
        notificationService.sendOrderConfirmation(order);

        return true; // 주문 처리 성공
    }
}

// 외부 시스템 인터페이스 (예시)
interface InventoryService {
    boolean checkInventory(List<OrderItem> items);
}

interface PaymentGateway {
    boolean processPayment(double amount, PaymentInfo paymentInfo);
}

interface ShippingService {
    void createShippingLabel(List<OrderItem> items, ShippingAddress shippingAddress);
}

interface NotificationService {
    void sendOrderConfirmation(Order order);
}

class PaymentInfo { /* ... */ }
class ShippingAddress { /* ... */ }

public class Client {
    public static void main(String[] args) {
        // ... Order 객체 생성 ...
        Order order = new Order( /* ... */ );
        PaymentInfo paymentInfo = new PaymentInfo();
        ShippingAddress shippingAddress = new ShippingAddress();

        InventoryService inventoryService = new RealInventoryService();
        PaymentGateway paymentGateway = new RealPaymentGateway();
        ShippingService shippingService = new RealShippingService();
        NotificationService notificationService = new RealNotificationService();

        OrderProcessor orderProcessor = new OrderProcessor(inventoryService, paymentGateway, shippingService, notificationService);

        if (orderProcessor.processOrder(order, paymentInfo, shippingAddress)) {
            System.out.println("주문 처리 완료");
        } else {
            System.out.println("주문 처리 실패");
        }
    }
}

// 실제 구현체 (예시)
class RealInventoryService implements InventoryService {
    @Override
    public boolean checkInventory(List<OrderItem> items) {
        // ... 실제 재고 확인 로직 ...
        return true;
    }
}

class RealPaymentGateway implements PaymentGateway {
    @Override
    public boolean processPayment(double amount, PaymentInfo paymentInfo) {
        // ... 실제 결제 처리 로직 ...
        return true;
    }
}

class RealShippingService implements ShippingService {
    @Override
    public void createShippingLabel(List<OrderItem> items, ShippingAddress shippingAddress) {
        // ... 실제 배송 정보 생성 로직 ...
        System.out.println("배송 정보 생성");
    }
}

class RealNotificationService implements NotificationService {
    @Override
    public void sendOrderConfirmation(Order order) {
        // ... 실제 알림 발송 로직 ...
        System.out.println("주문 완료 알림 발송");
    }
}

이 예시에서 OrderProcessor는 순수 가공 클래스입니다.

  • OrderProcessor는 도메인 모델의 핵심 개념(Order, OrderItem)을 직접적으로 나타내지는 않습니다.
  • 하지만 주문 처리라는 특정 책임을 수행하기 위해 만들어졌습니다.
  • OrderProcessor는 재고 관리, 결제 처리, 배송, 알림과 같은 외부 시스템과의 상호작용을 캡슐화하여 Order 클래스가 이러한 복잡성에 직접적으로 관여하지 않도록 합니다.

순수 가공의 장점:

  • 높은 응집도: Order 클래스는 주문 정보 관리에 집중하고, OrderProcessor는 주문 처리 로직에 집중하여 각 클래스의 응집도를 높입니다.
  • 낮은 결합도: Order 클래스는 외부 시스템에 직접적으로 의존하지 않고, OrderProcessor를 통해서만 간접적으로 상호작용하므로 결합도가 낮아집니다.
  • 재사용성: OrderProcessor는 다양한 상황에서 주문 처리 로직을 재사용할 수 있습니다.
  • 관심사 분리: 비즈니스 로직(주문 처리)과 도메인 모델(주문 정보)을 명확하게 분리하여 유지보수를 용이하게 합니다.

결론적으로, 순수 가공 패턴은 자연스러운 도메인 모델에 책임을 할당하기 어려울 때, 별도의 클래스를 도입하여 시스템의 설계 품질을 향상시키는 효과적인 방법입니다.