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로부터 보호받도록 합니다.
보호된 변이의 장점:
- 안정성: 예상되는 변경 사항이 시스템의 다른 부분에 미치는 영향을 최소화하여 시스템의 안정성을 높입니다.
- 유지보수성: 변경 사항이 특정 모듈 내에 격리되어 유지보수가 용이해집니다.
- 확장성: 새로운 기능을 추가하거나 기존 기능을 변경할 때 기존 코드를 수정할 위험을 줄여 시스템의 확장성을 향상시킵니다.
- 재사용성: 안정적인 인터페이스를 통해 다양한 구현체를 쉽게 교체하고 재사용할 수 있습니다.
결론적으로, 보호된 변이 패턴은 예상되는 변경 사항을 캡슐화하여 시스템을 더욱 견고하고 유지보수하기 쉽게 만드는 중요한 설계 원칙입니다. 인터페이스, 추상 클래스, 레이어링 등 다양한 메커니즘을 통해 구현될 수 있습니다.