프로젝트

일반

사용자정보

GRASP 패턴: Protected Variations (보호된 변이)

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

GRASP 패턴: Protected Variations (보호된 변이) - 간단한 설명과 예시

핵심 아이디어: 예상되는 변경 사항(변이)을 캡슐화하여 다른 부분에 영향을 주지 않도록 책임을 할당합니다. 불안정한 요소 주위에 안정적인 인터페이스를 만들어 변경의 영향을 격리시키는 것이 핵심입니다.

간단한 설명:

소프트웨어 시스템은 끊임없이 변화합니다. 요구사항이 변경되거나, 기술이 발전하거나, 외부 시스템이 교체될 수 있습니다. 보호된 변이 패턴은 이러한 예상되는 변화(변이)를 식별하고, 이 변화가 시스템의 다른 부분에 미치는 영향을 최소화하는 설계를 목표로 합니다. 이를 위해 변하는 부분을 인터페이스나 추상 클래스와 같은 안정적인 추상화 뒤에 숨깁니다. 클라이언트는 이 안정적인 인터페이스에 의존하고, 실제 구현체의 변화로부터 보호받게 됩니다.

예시:

다양한 결제 방식을 지원하는 온라인 쇼핑몰을 생각해 봅시다. 현재는 신용카드와 계좌 이체만 지원하지만, 앞으로 페이팔, 휴대폰 결제 등 새로운 결제 방식이 추가될 가능성이 높습니다. 만약 주문 처리 로직이 특정 결제 방식에 직접적으로 의존한다면, 새로운 결제 방식이 추가될 때마다 주문 처리 코드를 수정해야 할 것입니다. 이는 변경에 취약한 설계입니다.

보호된 변이 패턴을 적용하여 이 문제를 해결할 수 있습니다. 결제 처리 방식을 추상화하는 인터페이스를 만들고, 각 결제 방식에 대한 구체적인 구현체를 이 인터페이스를 통해 제공하는 것입니다.

// 결제 인터페이스 (안정적인 추상화)
interface PaymentGateway {
    boolean processPayment(double amount, PaymentInfo paymentInfo);
}

// 신용카드 결제 구현체 (변이될 수 있음)
class CreditCardPaymentGateway implements PaymentGateway {
    @Override
    public boolean processPayment(double amount, PaymentInfo paymentInfo) {
        // 신용카드 결제 처리 로직
        System.out.println("신용카드로 " + amount + "원 결제 처리");
        // ... 신용카드 결제 API 호출 ...
        return true;
    }
}

// 계좌 이체 결제 구현체 (변이될 수 있음)
class BankTransferPaymentGateway implements PaymentGateway {
    @Override
    public boolean processPayment(double amount, PaymentInfo paymentInfo) {
        // 계좌 이체 결제 처리 로직
        System.out.println("계좌 이체로 " + amount + "원 결제 처리");
        // ... 계좌 이체 API 호출 ...
        return true;
    }
}

// 페이팔 결제 구현체 (새로운 변이)
class PayPalPaymentGateway implements PaymentGateway {
    @Override
    public boolean processPayment(double amount, PaymentInfo paymentInfo) {
        // 페이팔 결제 처리 로직
        System.out.println("페이팔로 " + amount + "원 결제 처리");
        // ... 페이팔 API 호출 ...
        return true;
    }
}

// 주문 처리 클래스 (안정적인 PaymentGateway 인터페이스에 의존)
class OrderProcessor {
    private PaymentGateway paymentGateway;

    public OrderProcessor(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public boolean checkout(Order order, PaymentInfo paymentInfo) {
        double totalAmount = order.getTotalAmount();
        return paymentGateway.processPayment(totalAmount, paymentInfo);
    }
}

// 도메인 객체 (예시)
class Order {
    public double getTotalAmount() {
        return 10000; // 예시
    }
}

class PaymentInfo { /* 결제 관련 정보 */ }

public class Client {
    public static void main(String[] args) {
        Order order = new Order();
        PaymentInfo paymentInfo = new PaymentInfo();

        // 현재는 신용카드 결제 사용
        PaymentGateway creditCardGateway = new CreditCardPaymentGateway();
        OrderProcessor processorWithCreditCard = new OrderProcessor(creditCardGateway);
        processorWithCreditCard.checkout(order, paymentInfo);

        System.out.println("--- 결제 방식 변경 ---");

        // 새로운 결제 방식 (페이팔) 추가 및 사용
        PaymentGateway payPalGateway = new PayPalPaymentGateway();
        OrderProcessor processorWithPayPal = new OrderProcessor(payPalGateway);
        processorWithPayPal.checkout(order, paymentInfo);

        // 기존 주문 처리 코드 변경 없이 새로운 결제 방식 지원 가능
    }
}

이 예시에서 PaymentGateway 인터페이스가 보호된 변이의 역할을 합니다.

  • OrderProcessor는 구체적인 결제 방식 클래스 (CreditCardPaymentGateway, BankTransferPaymentGateway, PayPalPaymentGateway)에 직접적으로 의존하는 대신, 안정적인 PaymentGateway 인터페이스에 의존합니다.
  • 새로운 결제 방식이 추가되더라도 PaymentGateway 인터페이스를 구현하는 새로운 클래스 (PayPalPaymentGateway)만 만들면 됩니다. OrderProcessor 코드는 변경할 필요가 없습니다.
  • PaymentGateway 인터페이스는 다양한 결제 방식의 공통적인 행위 (processPayment)를 정의하여 클라이언트(OrderProcessor)가 각 결제 방식의 구체적인 구현 details로부터 보호받도록 합니다.

보호된 변이의 장점:

  • 안정성: 예상되는 변경 사항이 시스템의 다른 부분에 미치는 영향을 최소화하여 시스템의 안정성을 높입니다.
  • 유지보수성: 변경 사항이 특정 모듈 내에 격리되어 유지보수가 용이해집니다.
  • 확장성: 새로운 기능을 추가하거나 기존 기능을 변경할 때 기존 코드를 수정할 위험을 줄여 시스템의 확장성을 향상시킵니다.
  • 재사용성: 안정적인 인터페이스를 통해 다양한 구현체를 쉽게 교체하고 재사용할 수 있습니다.

결론적으로, 보호된 변이 패턴은 예상되는 변경 사항을 캡슐화하여 시스템을 더욱 견고하고 유지보수하기 쉽게 만드는 중요한 설계 원칙입니다. 인터페이스, 추상 클래스, 레이어링 등 다양한 메커니즘을 통해 구현될 수 있습니다.