본문 바로가기

스프링

[Spring] 디자인 패턴 - 전략 패턴

참고 1. 스프링-핵심-원리-고급편, 김영한

전략 패턴, Strategy Pattern

변하지 않는 부분을 "Context"에 두고, 변하는 부분을 "Strategy"라는 인터페이스에 둔다. 그리고, Context는 Stragtegy라는 인터페이스에만 의존한다. 이렇게 되면, 이후 새로운 Strategy를 변경하거나 생성할 때, Context에는 영향이 가지 않는다. 이러한 전략은 스프링의 의존관계 주입(DI)에서 사용하는 전략이다.

  • Context : 변하지 않는 템플릿 역할
  • Strategy : 변하는 알고리즘 역할

템플릿 메서드 vs 전략 패턴

템플릿 메서드는 부모 클래스에 변하지 않는 템플릿과 변하는 추상 메서드를 두어 관리한다. 하지만, 전략 패턴은 변하지 않는 부분을 Context에 두고, 변하는 부분을 Strategy라는 인터페이스에 따로 관리한다.

  • 템플릿 메서드 : 중복 문제를 상속(추상)으로 해결
  • 전략 패턴 : 중복 문제를 위임(인터페이스)으로 해결 

예시

1. Strategy 인터페이스 정의

public interface Strategy {
    void call();
}

2. Strategy를 의존하는 Context 정의

public class Context {
	
    // Strategy를 의존
    private Strategy strategy;

	// Strategy를 생성자 주입받아 사용
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void execute() {
        //변하지 않는 부분
        System.out.println("execute start");

        //로직마다 변하는 부분
        strategy.call(); //위임

        //변하지 않는 부분
        System.out.println("execute start");
    }
}

3. 원하는 로직들(변하는 로직들)을 정의

//로직1
public class StrategyLogic1 implements Strategy{
    @Override
    public void call() {
    	log.info("로직1");
    }
}

//로직2
public class StrategyLogic2 implements Strategy{
    @Override
    public void call() {
    	log.info("로직2");
    }
}

4. Context에 각 Strategy를 주입하고 실행

StrategyLogic1 strategyLogic1 = new StrategyLogic1();
Context context1 = new Context(strategyLogic1);

StrategyLogic2 strategyLogic2 = new StrategyLogic2();
Context context2 = new Context(strategyLogic2);

context1.execute();
context2.execute();

좀 더 축약해서 사용하는 법

void strategy() {
    //1. 익명 내부 클래스로 짧게
    Strategy logic1 = new Strategy() {
        @Override
            public void call() {
            log.info("로직 1 실행");
        }
    };
    Context context1 = new Context(logic1);
    context1.execute();

    //2. 더 짧게
    Context context2 = new Context(new Strategy() {
    @Override
    public void call() {
            log.info("로직 2 실행");
        }
    });
    context2.execute();

    //3. 더 짧게
    new Context(new Strategy() {
    @Override
    public void call() {
            log.info("로직 2 실행");
        }
    }).execute();

    //4. lambda를 활용해 더 짧게
    Context context3 = new Context(() -> log.info("로직 1 실행"));
    context1.execute();

    //5. 더 짧게
    new Context(() -> log.info("로직 1 실행")).execute();
}
*인터페이스에 메서드가 하나만 있으면 Lambda로 사용 가능하다.