도찐개찐

[코드디자인패턴-JAVA] Strategy Pattern 본문

프로그래밍/코드디자인패턴

[코드디자인패턴-JAVA] Strategy Pattern

도개진 2024. 2. 29. 09:24

Strategy Pattern

사용시점

  1. 여러 알고리즘 중 선택 가능할 때: 같은 문제를 해결하는 데 여러 가지 방법이 있을때, 알고리즘을 쉽게 변경하거나 확장하고자 할 때 사용 합니다.
  2. 알고리즘 변경이 클라이언트 고트에 영향을 미치지 않아야 할 때: 전략 패턴을 사용하면 알고리즘을 변경해도 클라이언트 코드를 변경할 필요가 없게 됩니다.
  3. 클래스의 행위를 동적으로 변경하고 싶을 때: 실행 중에 동적으로 특정 행위를 변경하고자 할 때 사용할 수 있습니다.

종류

Strategy Pattern 자체는 특정한 "종류"가 있는 것이 아니라, 알고리즘의 행동을 인터페이스로 정의하고, 이를 구현하는 여러 전략 클래스를 제공하는 구조입니다.
  • 정렬 알고리즘을 캡슐화한 전략(버블 정렬, 퀵 정렬, 병합 정렬 등)
  • 할인 정책을 캡슐화한 전략(고객 등급별 할인, 특별 이벤트 할인 등)
  • 인증 방식을 캡슐화한 전략(OAuth 인증, 토큰 인증 등)

특징

  • 특정 컨텍스트에 따라 적합한 전략을 선택, 실행
  • 코드의 유연성과 재사용성을 향상
  • 특정 작업을 수행하는 알고리즘이나 행위를 캡슐화
  • 작업을 수행하는 전략을 쉽게 교체할 수 있도록 하는 디자인 패턴
// 전략 인터페이스
public interface PaymentStrategy {
    void pay(int amount);
}
// 신용카드를 사용하는 전략
public class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String name;

    public CreditCardStrategy(String cardNumber, String name) {
        this.cardNumber = cardNumber;
        this.name = name;
    }
    [Strategy Pattern.html](Strategy%20Pattern.html)
    @Override
    public void pay(int amount) {
        System.out.println("신용 카드로 " + amount + "원을 지불 했습니다."); 
    }
}
// PayPal을 사용하는 전략
public class PaypalStrategy implements PaymentStrategy {
    private String email;

    public PaypalStrategy(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println("PayPal로 " + amount + "원을 지불 했습니다.");
    }
}
// 결제 컨텍스트
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public ShoppingCart(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart1 = new ShoppingCart(new CreditCardStrategy("1234-5678-1234-5678", "홍길동"));
        cart1.checkout(5000);

        ShoppingCart cart2 = new ShoppingCart(new PaypalStrategy("dev-truly@example.com"));
        cart2.checkout(31000);
    }
}

Main.main(null)
신용 카드로 5000원을 지불 했습니다.
PayPal로 31000원을 지불 했습니다.
위의 예시에서 PaymentStrategy 인터페이스는 다양한 결제 전략을 정의하고,
CreditCardStrategy와 PaypalStrategy 클래스는 이 인터페이스를 구현합니다.
ShoppingCart 클래스는 결제 방식을 추상화하고, 전략을 변경하거나 확장하기 쉽게 만들어 줍니다.

Stragegy Pattern 사용 대표 자바 라이브러리

Comparator 인터페이스는 객체를 비교하는 다양한 전략을 정의 하는 데 사용 될 수 있으며,

이는 Collections.sort와 같은 정렬 메서드에서 사용 될 수 있습니다.

import java.util.Arrays;
import java.util.Comparator;

public class StrategyExample {
    public static void main(String[] args) {
        Integer[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6 };

        // 익명 클래스를 사용한 전략
        Arrays.sort(numbers, new Comparator<Integer>() {
            public int compare(Integer a, Integer b) {
                return a - b; // 오름차순 정렬
            }
        });
        for (int number : numbers) System.out.print(number + " ");

        System.out.println("");
        // 람다를 사용한 전략 (Java 8 이상)
        Arrays.sort(numbers, (a, b) -> b - a); // 내림차순 정렬
        for (int number : numbers) System.out.print(number + " ");
    }
}

StrategyExample.main(null)
1 1 2 3 4 5 6 9 
9 6 5 4 3 2 1 1 

장점

  1. 유연성: 전략 패턴을 사용하면 알고리즘을 쉽게 변경하거나 확장 할 수 있습니다. 새로운 전략을 추가 하려면 기존 코드를 변경할 필요가 없으므로 유지보수가 쉽습니다.
  2. 재사용성: 전략 객체는 여러 컨텍스트에서 재사용 될 수 있습니다. 동일한 알고리즘이 여러곳에서 필요한 경우 전략 패턴을 사용하면 코드 중복을 피할 수 있습니다.
  3. 의존성분리: 알고리즘과 그것을 사용하는 클라이언트 코드 간의 결합도를 낮춥니다. 이로 인해 시스템의 각 부분이 독립적으로 변경하고 테스트 할 수 있게 됩니다.

단점

  1. 복잡도 증가: 전략 패턴을 사용하면 여러 전략 클래스와 컨텍스트 클래스가 추가 될 수 있으므로, 설계가 복잡해 질 수 있습니다. 필요 이상으로 사용 될 경우 이해하고 유지 보수하기 어려울 수 있습니다.
  2. 런타임 오버헤드: 전략 패턴은 객체 지향적인 접근 방식이므로 런타임에 추가 메모리와 프로세싱 오버헤드가 발생 할 수 있습니다. 성능이 매우 중요한 경우에는 이 패턴의 사용이 제한 될 수 있습니다.

결론

  • 알고리즘의 변경과 확장, 재사용성 향상, 결함도 감소 이점
  • 설계의 복잡성, 런타임 오버헤드 등을 고려 필요
728x90
Comments