Mobile & App Stack/Java Software Architecture & Patterns

전략 패턴(Strategy Pattern) 완벽 정리: if-else 지옥에서 탈출하기

임베디드 친구 2024. 12. 30. 08:01
728x90
반응형

새로운 기능이 추가될 때마다 늘어나는 if-else 문 때문에 코드를 읽기 어려웠던 적이 있으신가요? 기능이 바뀔 때마다 기존 코드를 수정해야 한다면 그것은 객체지향 설계의 신호등에 빨간불이 켜진 것입니다.

오늘은 동적으로 행동을 바꾸면서도 코드는 깔끔하게 유지할 수 있는 전략 패턴(Strategy Pattern)에 대해 깊이 있게 알아보겠습니다.

Generated by Gemini AI.


1. 전략 패턴이란?

전략 패턴은 특정 행동(알고리즘)을 직접 구현하지 않고, 개별적인 클래스로 캡슐화하여 상황에 따라 교체하며 사용할 수 있게 만드는 패턴입니다.

핵심 원칙

  • 상속보다는 구성(Composition): 클래스를 상속받아 기능을 확장하는 대신, 인터페이스를 통해 전략을 '갈아 끼우는' 방식을 취합니다.
  • OCP (개방-폐쇄 원칙): 기존의 Context 코드를 변경하지 않고도 새로운 전략을 무한히 추가할 수 있습니다.

2. 전략 패턴의 구조

이 패턴은 세 가지 핵심 요소로 구성됩니다.

  1. Strategy (인터페이스): 모든 전략 클래스가 구현해야 하는 공통의 인터페이스입니다.
  2. ConcreteStrategy (구체적인 전략): 실제로 동작하는 알고리즘을 구현한 클래스들입니다.
  3. Context (컨텍스트): 전략을 사용하는 주체입니다. 어떤 전략을 쓸지 결정하는 '전략 객체'를 가지고 있습니다.

3. Java 구현 예제: 다중 결제 시스템

사용자가 결제 시점에 신용카드, 카카오페이, 네이버페이 중 하나를 선택하는 상황을 구현해 보겠습니다.

Step 1. 전략 인터페이스와 구체적 전략

Java
 
// Strategy 인터페이스
interface PaymentStrategy {
    void pay(int amount);
}

// 구체적인 전략 1: 신용카드
class CardPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println(amount + "원을 신용카드로 결제합니다.");
    }
}

// 구체적인 전략 2: 카카오페이
class KakaoPayPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println(amount + "원을 카카오페이로 결제합니다.");
    }
}

Step 2. Context 클래스 (쇼핑카트)

Java
 
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    // 실행 중에 전략을 설정(변경)할 수 있음
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }

    public void checkout(int amount) {
        if (paymentStrategy == null) {
            System.out.println("결제 수단을 선택해주세요.");
            return;
        }
        paymentStrategy.pay(amount);
    }
}

Step 3. 클라이언트 코드

Java
 
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 사용자가 신용카드를 선택한 경우
        cart.setPaymentStrategy(new CardPayment());
        cart.checkout(10000);

        // 사용자가 결제 수단을 카카오페이로 변경한 경우
        cart.setPaymentStrategy(new KakaoPayPayment());
        cart.checkout(20000);
    }
}

4. 전략 패턴의 장단점

👍 장점

  • 런타임 유연성: 프로그램 실행 중에 알고리즘을 자유롭게 바꿀 수 있습니다.
  • 코드 가독성: 복잡한 조건문(if-else)이 사라지고 로직이 명확해집니다.
  • 테스트 용이성: 각 전략이 독립된 클래스이므로 단위 테스트가 매우 쉽습니다.

👎 단점

  • 객체 수 증가: 전략이 많아질수록 관리해야 할 클래스 수도 늘어납니다.
  • 사용자의 이해도: 클라이언트 코드가 어떤 전략들이 있는지 알고 있어야 적절한 선택이 가능합니다.

5. 실무 활용 및 꿀팁 (Java 8+)

최근 자바에서는 인터페이스의 메서드가 하나인 경우(Functional Interface), 굳이 클래스를 만들지 않고 람다식(Lambda)을 사용하여 더욱 간결하게 전략 패턴을 적용할 수 있습니다.

Java
 
// 클래스 생성 없이 즉석에서 전략 정의
cart.setPaymentStrategy(amount -> System.out.println(amount + "원 포인트 결제"));

결론

전략 패턴은 "변하는 것과 변하지 않는 것을 분리하라"는 객체지향의 대원칙을 가장 잘 보여주는 패턴입니다. 요구사항이 수시로 변하는 실무 환경에서 전략 패턴을 적절히 활용한다면, 유지보수가 즐거운 코드를 작성할 수 있을 것입니다.


포스팅이 도움이 되셨다면 공감과 구독 부탁드립니다! 여러분은 실무에서 어떤 if 문을 전략 패턴으로 바꾸고 싶으신가요? 댓글로 공유해 주세요!

반응형