제리 devlog

[디자인 패턴] 전략 패턴 (Strategy Pattern) 본문

디자인 패턴

[디자인 패턴] 전략 패턴 (Strategy Pattern)

제리 . 2022. 3. 6. 21:26

전략 패턴 이란?

알고리즘 제품군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만들자. 전략을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있다. [GOF]

전략 패턴은 이전에 포스팅한 템플릿 메서드 패턴과 유사하다.

변하지 않는 부분과 변하는 부분이 분리되고 인터페이스를 통해 구현된다.

 

비즈니스 로직의 실행 시간을 측정하는 예시이다.

여기서 변하는 부분은 비즈니스 로직, 변하지 않는 부분은 실행 시간을 측정하는 부분이다.

 

strategy는 변하는 알고리즘을 담당한다.

fun interface Strategy {
    fun call()
}

 

context는 변하지 않는 부분을 의미한다.

class Context(private val strategy: Strategy) {

    private val logger = LoggerFactory.getLogger(this::class.simpleName)

    fun execute() {
        val startTime = System.currentTimeMillis()
        strategy.call()
        val endTime = System.currentTimeMillis()
        val resultTime = endTime - startTime
        logger.info("resultTime=$resultTime")
    }
}

execute()에서 변하는 알고리즘을 의미하는 strategy가 위임을 통해 호출되고 있다.

Context 클래스는 strategy를 구현한 내용(변하는 알고리즘)이 변하지 않는 부분에서 실행되는 맥락을 보여준다. 

 

그리고 알고리즘을 구현한 각각의 클래스를 만들어 준다. 

class StrategyLogic1 : Strategy {

    private val logger = LoggerFactory.getLogger(this::class.simpleName)

    override fun call() {
        logger.info("비즈니스 로직 1실행")
    }
}
class StrategyLogic2 : Strategy {

    private val logger = LoggerFactory.getLogger(this::class.simpleName)

    override fun call() {
        logger.info("비즈니스 로직 2실행")
    }
}

전략 패턴은 다음과 같이 호출해서 사용할 수 있다.

@Test
fun `전략 패턴 적용`() {
    val strategyLogic1: Strategy = StrategyLogic1()
    val context1 = Context(strategyLogic1)
    context1.execute()

    val strategyLogic2: Strategy = StrategyLogic2()
    val context2 = Context(strategyLogic2)
    context2.execute()
}

@Test
fun `람다를 사용한 전략 패턴 적용 `() {
    val strategyLogic = Strategy { logger.info("비즈니스 로직 실행") }
    val context = Context1(strategyLogic)
    context.execute()
}

1. 각각의 전략(변하는 알고리즘은) strategy를 구현한 구현체로 생성된다.

2. context에 전략을 생성자로 넘긴다(혹은 execute에 전략을 파라미터로 넘긴다)

3. context에서는 전달받은 전략을 사용해서 알고리즘이 실행된다.

 

전략 패턴은 상속을 사용하여 구현된 템플릿 메서드 패턴과 달리 인터페이스와 위임을 통해 구현된다.

전략 패턴의 핵심은 context가 strategy인터페이스만 의존한다는 점이다. 상속과는 달리 strategy의 구현체가 변경되거나 새로 만들어도 context코드에 영향을 주지 않는다.

스프링에서는 생성자 주입을 하는 방식이 전략 패턴을 사용한다.

Comments