μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό ν™œμš©ν•œ μ „λž΅νŒ¨ν„΄

1. μ „λž΅ νŒ¨ν„΄μ΄λž€

μ „λž΅ νŒ¨ν„΄μ€ μ•Œκ³ λ¦¬μ¦˜μ„ 그룹으둜 μ •μ˜ν•˜κ³ , 각 그룹을 λ³„λ„μ˜ ν΄λž˜μŠ€μ—μ„œ κ΄€λ¦¬ν•˜λ©° 그룹의 μ•Œκ³ λ¦¬μ¦˜μ„ μ„ νƒμ μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•˜λŠ” 행동 λ””μžμΈνŒ¨ν„΄μ΄λ‹€.

 

μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ 및 JAVA API λ‚΄μ—μ„œλ„ λΉˆλ²ˆν•˜κ²Œ μ°Ύμ•„λ³Ό 수 μžˆλŠ” λ””μžμΈ νŒ¨ν„΄μ΄λ‹€.

 

그리고 μŠ€ν”„λ§μ„ μ‚¬μš©ν•˜κ³  μžˆλ‹€λ©΄ 빈 μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‚¬μš©ν•˜μ—¬ ν•΄λ‹Ή μ „λž΅νŒ¨ν„΄μ„ κΉ”λ”ν•˜κ²Œ μ μš©ν•  수 μžˆλŠ”λ° νŠΉμ • μ„œλΉ„μŠ€μ˜ ν΄λž˜μŠ€λ‚˜ λ©”μ†Œλ“œκ°€ κ³Όλ„ν•˜κ²Œ λ¬΄κ±°μ›Œ μ§€λŠ”κ²ƒμ„ μ˜ˆλ°©ν•  수 μžˆλ‹€.

 

2. 적용 예제

기쑴에 νšŒμ‚¬μ—μ„œ μ§€μ›ν•˜λŠ” 결제 μˆ˜λ‹¨μ΄ λ§Žμ§€ μ•Šμ•˜λŠ”λ°, μ΄λ²ˆμ— μ‹ κ·œ μ•± 런칭과 λ”λΆˆμ–΄ λ‹€μ–‘ν•œ 결제 μˆ˜λ‹¨μ„ μ§€μ›ν•˜κ²Œ λ˜μ—ˆλ‹€.
μ •μ‚° λͺ¨λ“ˆλ„ 그에 λ§žμΆ”μ–΄ μ—¬λŸ¬ κ²°μ œμˆ˜λ‹¨μ— λ”°λ₯Έ λΉ„μ§€λ‹ˆμŠ€ λ‘œμ§μ„ μ†Œν™”ν•˜λ„λ‘ λ³€κ²½ν•΄μ•Ό ν•˜λŠ” μƒν™©μ΄μ—ˆλ‹€.

기쑴에 μ§€μ›ν•˜λ˜ 결제 μˆ˜λ‹¨μ„ νŽΈμ˜μƒ A, B, C 라고 μΉ­ν•˜κ² λ‹€.

졜초 결제 μˆ˜λ‹¨μ„ 늘리렀던 μ‹œμ μ—μ„œλŠ” λ‹¨μˆœν•˜κ²Œ 뢄기문을 톡해 A,B,C κ²°μ œμˆ˜λ‹¨μ„ λΆ„κΈ°ν•˜λ„λ‘ μž‘μ„±λ˜μ–΄ μžˆμ—ˆλ‹€.

@Service
@RequiredArgsConstructor
public class purchaseService {

    private final PaymentMethodAService aService;
    private final PaymentMethodBService bService;
    private final PaymentMethodCService cService;

    public Result purchase(PurchaseInfo purchaseInfo) {

        PaymentMethod paymentMethod = purchaseInfo.getPaymentMethod();

        if (paymentMethod == PaymentMethod.A) {
            return aService.pay(purchaseInfo);
        } else if (paymentMethod == PaymentMethod.B) {
            return bService.pay(purchaseInfo);
        } else if (paymentMethod == PaymentMethod.C) {
            return cService.pay(purchaseInfo);
        } else {
            throw new IllegalArgumentException("payment method not found");
        }

    }

    public Result refund(RefundInfo refundInfo) {

        PaymentMethod paymentMethod = refundInfo.getPaymentMethod();

        if (paymentMethod == PaymentMethod.A) {
            return aService.refund(refundInfo);
        } else if (paymentMethod == PaymentMethod.B) {
            return bService.refund(refundInfo);
        } else if (paymentMethod == PaymentMethod.C) {
            return cService.refund(refundInfo);
        } else {
            throw new IllegalArgumentException("payment method not found");
        }

    }

}

 

μœ„ μ½”λ“œλ₯Ό 보면 μš°μ„  얼핏 봐도 μ„Έ 가지 μ •λ„μ˜ 문제점이 보인닀.

  1. 결제 μˆ˜λ‹¨μ΄ μΆ”κ°€ λ˜λŠ” 경우 κ³ μΉ  μ½”λ“œκ°€ λ„ˆλ¬΄ λ§Žλ‹€.
  2. 결제 κ΄€λ ¨ κΈ°λŠ₯이 μΆ”κ°€λ˜λŠ” 경우 κ³ μΉ  μ½”λ“œκ°€ λ„ˆλ¬΄ λ§Žλ‹€.
  3. 결제 μˆ˜λ‹¨μ΄ λŠ˜μ–΄λ‚  λ•Œ λ§ˆλ‹€ μ˜μ‘΄μ„± λ˜ν•œ λŠ˜μ–΄λ‚œλ‹€. (λ‹¨μœ„ν…ŒμŠ€νŠΈ μ‹œ λΆˆν•„μš”ν•œ μ½”λ“œμž‘μ„± μ‹œκ°„ 증가)

이제 λ‹€μŒ μ½”λ“œλ₯Ό μ „λž΅ νŒ¨ν„΄μ„ μ μš©ν•¨μœΌλ‘œμ¨ λ¬Έμ œμ μ„ ν•΄κ²°ν•΄λ³΄μž.

 

3. 적용

μš°μ„  κ³΅ν†΅μ μœΌλ‘œ 결제, ν™˜λΆˆ λ“± 핡심적인 λ‚΄μš©μ„ 좔상화 ν•˜μ—¬ μΈν„°νŽ˜μ΄μŠ€λ‘œ λ§Œλ“ λ‹€.

public interface PaymentMethodService {

    Result purchase(PurchaseInfo purchaseInfo);
    Result refund(RefundInfo refundInfo);

}

 

그리고 결제 μˆ˜λ‹¨λ“€μ΄ ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λ₯Ό 상속받도둝 λ³€κ²½ν•œλ‹€.

@Service("AMethod")
public class PaymentMethodAService implements PaymentMethodService {
	//...
}
@Service("BMethod")
public class PaymentMethodBService implements PaymentMethodService {
	//...
}

 

λ‹€μŒκ³Ό 같이 μ—¬λŸ¬ ν΄λž˜μŠ€κ°€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 상속 λ°›μœΌλ©΄ Bean μ£Όμž… μ‹œ `Map` μ΄λ‚˜ `List` 와 같은 μ»¬λ ‰μ…˜ ν˜•νƒœλ‘œ 객체듀을 κ°€μ Έμ˜¬ 수 μžˆλ‹€.
λ”°λΌμ„œ 처음 μ½”λ“œλ₯Ό λ‹€μŒκ³Ό 같이 μˆ˜μ • κ°€λŠ₯ν•˜λ‹€.

 

@Service
@RequiredArgsConstructor
public class purchaseService {

    private final Map<String, PaymentMethodService> paymentMethodServiceMap;
    
    public Result purchase(PurchaseInfo purchaseInfo) {        
        String paymentMethod = purchaseInfo.getPaymentMethod().getName();

        return paymentMethodServiceMap.computeIfAbsent(paymentMethod, key -> {
            throw new IllegalArgumentException("payment method not found");
        }).pay(purchaseInfo);
    }



    public Result refund(RefundInfo refundInfo) {

        String paymentMethod = refundInfo.getPaymentMethod().getName();
        
        return paymentMethodServiceMap.computeIfAbsent(paymentMethod, key -> {
        	throw new IllegalArgumentException("payment method not found");
        }).refund(refundInfo);
    }
}

λ‹€μŒκ³Ό 같이 μ „λž΅ νŒ¨ν„΄μ„ 적용 ν•¨μœΌλ‘œμ¨ μ•žμ„œ 닀룬 3가지 문제점이 κΉ”λ”ν•˜κ²Œ ν•΄κ²°λ˜μ—ˆλ‹€.

이 이후 결제 μˆ˜λ‹¨μ΄ 계속 μΆ”κ°€λ˜μ–΄λ„, κΈ°μ‘΄ μ½”λ“œμ˜ μˆ˜μ • 없이 PaymentMethodService λ₯Ό μƒμ†λ°›λŠ” 클래슀만 μΆ”κ°€ν•¨μœΌλ‘œμ¨ μ•ˆμ „ν•˜κ³  νŽΈλ¦¬ν•œ ν™•μž₯이 κ°€λŠ₯ ν•΄μ‘Œλ‹€. πŸ‘