스택큐힙리스트

코틀린 Strategy 패턴 한방에 이해하기 – 알고리즘을 ‘핫스왑’하는 가장 간단한 방법 본문

개발

코틀린 Strategy 패턴 한방에 이해하기 – 알고리즘을 ‘핫스왑’하는 가장 간단한 방법

스택큐힙리스트 2025. 7. 21. 22:34
반응형

1. 왜 Strategy 패턴인가?

“결제 수수료 정책이 또 바뀌었대요.”
매번 if … else … 문을 고쳐 배포했다면 이제는 Strategy 패턴으로 갈아타 보세요.
행위를 인터페이스(전략)로 분리해 두면 런타임에 원하는 구현으로 교체할 수 있어 코드 가독성·테스트 용이성이 모두 상승합니다. 이를테면 신용카드 → 계좌이체 → 간편결제 로직을 바꿀 때 클래스만 새로 만들어 DI(Container)가 끼워 넣어 주면 끝!


2. 핵심 구조 (말로 툭 설명)

  • Strategy: 알고리즘 규약을 담은 인터페이스
  • ConcreteStrategy: 실제 알고리즘 클래스들
  • Context: Strategy를 받아 행위를 위임하고, 필요하면 전략을 교체

3. Kotlin 예제 – 결제 수수료 계산기

// 1) 전략 인터페이스
fun interface FeePolicy {
    fun fee(amount: Int): Int
}

// 2) 구체 전략
class CardFeePolicy : FeePolicy {
    override fun fee(amount: Int) = (amount * 0.03).toInt()  // 3% 카드 수수료
}

class BankTransferFeePolicy : FeePolicy {
    override fun fee(amount: Int) = (amount * 0.01).toInt()  // 1% 계좌이체 수수료
}

// 3) 컨텍스트
class PaymentService(private var feePolicy: FeePolicy) {

    fun changePolicy(newPolicy: FeePolicy) {
        this.feePolicy = newPolicy           // 런타임 전략 교체
    }

    fun pay(amount: Int): Int {
        val fee = feePolicy.fee(amount)
        println("결제 금액: $amount, 수수료: $fee")
        return amount + fee
    }
}

// --- 사용 예시 ---
fun main() {
    val payment = PaymentService(CardFeePolicy())
    payment.pay(10_000)              // 카드 기준

    payment.changePolicy(BankTransferFeePolicy()) // 전략 교체
    payment.pay(10_000)              // 계좌이체 기준
}

코틀린의 함수형 인터페이스(fun interface) 덕분에 람다로도 전략을 가볍게 전달할 수 있습니다 – changePolicy { amt -> (amt * 0.05).toInt() } 처럼요.


4. 장·단점 체크리스트

  • 👍 장점
    • OCP 준수: 새 정책 추가 시 기존 클래스 수정 없음
    • 테스트 수월: Mockito stub로 전략만 갈아끼워 시나리오 검증
    • 조합 가능: 데코레이터·팩토리와 함께 쓰면 선택 로직이 더 우아
  • ⚠️ 단점
    • 전략 클래스가 많아지면 파일 수 폭증
    • 단순 분기라면 when 문이 오히려 직관적일 수도

5. 실전 팁

  • 스프링 DI: @Component + @Qualifier로 전략을 자동 주입하면 컨텍스트가 훨씬 깔끔합니다.
  • 함수 타입 전략: 작은 로직이면 클래스를 생략하고 (Int) -> Int 람다로 전달해 boilerplate를 줄이세요.
  • 테스트 우선: Context의 changePolicy()와 pay() 호출을 먼저 테스트로 고정해 두면 전략 교체에도 마음이 편합니다.
반응형
Comments