티스토리 뷰

 

전략 패턴이란?

 

객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고 사용하며, 동적으로 전략 수정이 가능한 패턴입니다.

풀어서 설명한다면, 실행(런타임) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있는 패턴입니다.

 

 

 

전략 패턴 vs 템플릿 메서드 패턴

 

두 패턴 모두 알고리즘을 실행(런타임) 중에 알고리즘을 동적으로 적용한다는 공통점이 있지만

이번 배울 전략 패턴은 합성(composition)을 통해 해결하고 템플릿 메서드 패턴은 상속(inheritance)을 통해 해결합니다.

 

이해하기 쉽게 말하면 전략 패턴은 Interface를 의존하여 OCP인 개방 폐쇄 원칙을 잘 지키며 앞으로도 많이 사용될 디자인 패턴이지만 템플릿 메서드 패턴은 부모 클래스를 상속받아 부모 클래스의 코드를 의존하여 재사용한다는 점에서 결합도가 높은 패턴입니다.

 

 

 

 

 

전략 패턴의 구성 요소

 

전략 패턴은 다음과 같은 구성요소로 이루어져 있습니다.

 

  • 전략(Strategy) : 다양한 알고리즘을 정의하는 인터페이스 또는 추상 클래스입니다.
  • 구체적인 전략(Concrete Strategy) : 전략을 구현한 클래스로, 실제 알고리즘을 포함합니다.
  • 컨텍스트(Context) : 전략을 사용하는 클래스로, 전략 객체를 갖고 있고 클라이언트에 제공됩니다.

 

전략 패턴 구성요소 이미지
그림1 전략 패턴 구성요소

 

 

 

 

 

 

예시 코드 이미지 트레이닝

 

  • 결제 방법 전략 PaymentStrategy에는 카드 결제, 네이버 페이 결제를 정의합니다.
  • 배송 방법 전략 DeliveryStrategy에는 걸어서 배송, 트럭타고 배송을 정의합니다.
  • setter를 이용하면 실행(런타임) 중에 전략을 변경할 수 있습니다.

 

전략 패턴 코드
그림2 전략 패턴 코드

 

 

구체화 전략 수정할 때

전략을 구현한 코드에 로직이 변경하여도 ConcreteStrategy 만 변경하면 됩니다.

예를 들어, CardPaymentStrategy에 "(실물) 카드로 n원 결제합니다""핸드폰에 있는 가상 카드로 n원 결제합니다"로 바꾸기 위해서는 CardPaymentStrategy의 Override된 pay메서드 로직만 변경하면 쉽게 해결 됩니다.

 

이것이 가능한 이유는 Interface만 의존하며 결합도는 낮고 응집도가 높기 때문입니다.

 

 

 

전략 추가할 때

  • OCP인 개방 폐쇄의 원칙을 지킬 수 있으며 확장에 편리합니다.
  • 새로운 전략을 추가한다해도 이전 코드를 건들일 필요는 없고 영향도 가지 않습니다.

전략 패턴 확장 이미지
그림3 전략 패턴 확장

 

 

 

 

 

 

예시 코드

 

코드가 길어서 더보기 처리하였습니다.

 

주문 전략과 구체화 전략

더보기
// 각각 다른 클래스 입니다.
public interface PaymentStrategy {
    void pay(long amount);
}


// 각각 다른 클래스 입니다.
public class NaverPayPayStrategy implements PaymentStrategy{
    @Override
    public void pay(long amount) {
        System.out.println("네이버 페이로"  + amount + "원 결제합니다.");
    }
}


// 각각 다른 클래스 입니다.
public class CardPaymentStrategy implements PaymentStrategy{
    @Override
    public void pay(long amount) {
        System.out.println("카드로 " + amount + "원 결제 합니다.");
    }
}

 

 

배송 전략과 구체화 전략

더보기
// 각각 다른 클래스 입니다.
public interface DeliveryStrategy {
    void deliver(String address);
}


// 각각 다른 클래스 입니다.
public class TruckDeliveryStrategy implements DeliveryStrategy{
    @Override
    public void deliver(String address) {
        System.out.println(address + "로 트럭으로 배달합니다.");
    }
}


// 각각 다른 클래스 입니다.
public class WalkDeliveryStrategy implements DeliveryStrategy{
    @Override
    public void deliver(String address) {
        System.out.println(address + "로 걸어서 배달합니다.");
    }
}

 

 

배송 속도 전략과 구체화 전략

더보기
// 각각 다른 클래스 입니다.
public interface SpeedStrategy {
    void speed();
}


// 각각 다른 클래스 입니다.
public class NormalSpeedStrategy implements SpeedStrategy{
    @Override
    public void speed() {
        System.out.println("일반 속도로 배달합니다.");
    }
}


// 각각 다른 클래스 입니다.
public class RocketSpeedStrategy implements SpeedStrategy{
    @Override
    public void speed() {
        System.out.println("로켓 속도로 배달합니다.");
    }
}

 

 

Context 코드 (Order)

더보기
public class Order {

    private PaymentStrategy paymentStrategy;
    private DeliveryStrategy deliveryStrategy;
    private SpeedStrategy speedStrategy;

    public Order(PaymentStrategy paymentStrategy,
                 DeliveryStrategy deliveryStrategy,
                 SpeedStrategy speedStrategy) {
        this.paymentStrategy = paymentStrategy;
        this.deliveryStrategy = deliveryStrategy;
        this.speedStrategy = speedStrategy;
    }

    public void payOff(long amount){
        paymentStrategy.pay(amount);
    }

    public void deliver(String address){
        deliveryStrategy.deliver(address);
    }

    public void speed(){
        speedStrategy.speed();
    }

    // 런타임 중 전략 변경 가능
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 런타임 중 전략 변경 가능
    public void setDeliveryStrategy(DeliveryStrategy deliveryStrategy) {
        this.deliveryStrategy = deliveryStrategy;
    }

    // 런타임 중 전략 변경 가능
    public void setSpeedStrategy(SpeedStrategy speedStrategy) {
        this.speedStrategy = speedStrategy;
    }
}

 

 

 

 

 

테스트 코드와 결과

 

Context에 원하는 Strategy(전략) 구현체를 주입합니다.

@Test
void execute(){
    Order order = new Order(new CardPaymentStrategy(), 
    			new TruckDeliveryStrategy(),
            		new RocketSpeedStrategy());
    order.payOff(10000);
    order.deliver("서울시 송파구 잠실");
    order.speed();
}

/* 출력 결과
    카드로 10000원 결제 합니다.
    서울시 송파구 잠실로 트럭으로 배달합니다.
    로켓 속도로 배달합니다.
*/

 

 

번외) 익명 내부 클래스와 람다로 전략 패턴 사용하기

@Test
void execute(){
    Order order = new Order((long amount) -> log.info(amount + "원 결제합니다."), 
    			(String address) -> log.info(address + "로 배달합니다."), 
                	() -> log.info("배송!"));
    order.payOff(20000);
    order.deliver("서울시 강남구 강남");
    order.speed();
}

/** 출력 
    20000원 결제합니다.
    서울시 강남구 강남로 배달합니다.
    배송!
*/

 

 

감사합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함