[디자인 패턴] 템플릿 메서드 패턴 (Template Method Pattern)
템플릿 메서드 패턴이란?
템플릿 메서드 디자인 패턴의 목적은 다음과 같습니다. > "작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기합니다. 템플릿 메서드를 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의할 수 있습니다." [GOF]
좋은 설계는 변하는 것과 변하지 않는 것을 분리하는 것이다.
템플릿 메서드 패턴은 알고리즘의 골격인 템플릿을 제공하고 하위 클래스에서는 알고리즘의 특정 단계를 정의한다.
알고리즘의 특정 단계를 하위 클래스에서 정의하면 알고리즘의 구조를 변경하지 않고 특정 단계를 재 정의할 수 있다.
템플릿 메서드 패턴은 상속을 활용하여 변하는 부분과 변하지 않는 부분을 분리해준다.
비즈니스 로직의 실행 시간을 측정하는 예시이다.
여기서 변하는 부분은 비즈니스 로직, 변하지 않는 부분은 실행 시간을 측정하는 부분이다.
abstract class AbstractTemplate {
private val logger = LoggerFactory.getLogger("AbstractTemplate")
fun execute() {
val startTime = System.currentTimeMillis()
call()
val endTime = System.currentTimeMillis()
val resultTime = endTime - startTime
logger.info("resultTime=$resultTime")
}
abstract fun call()
}
추상 클래스로 AbstractTemplate이 선언되어있다. excute에는 실행 시간을 측정하는 로직이 있고, 비즈니스 로직은 call이라는 추상 메서드로 분리되어있다.
AbstractTemplate를 상속받는 클래스를 선언하고 비즈니스 로직(call)을 구현해준다.
class SubClassLogic1 : AbstractTemplate() {
private val logger = LoggerFactory.getLogger(this::class.simpleName)
override fun call() {
logger.info("비즈니스 로직 1실행")
}
}
class SubClassLogic2 : AbstractTemplate() {
private val logger = LoggerFactory.getLogger(this::class.simpleName)
override fun call() {
logger.info("비즈니스 로직 2실행")
}
}
템플릿 메서드 패턴은 아래와 같이 사용한다.
@Test
fun `템플릿 메서드 패턴 적용`() {
val template1: AbstractTemplate = SubClassLogic1()
template1.execute()
val template2: AbstractTemplate = SubClassLogic2()
template2.execute()
}
@Test
fun `익명 클래스를 사용한 템플릿 메서드 패턴 적용`() {
val template: AbstractTemplate = object : AbstractTemplate() {
override fun call() {
logger.info("비즈니스 로직 실행")
}
}
template.execute()
}
1. AbstractTemplate을 상속받아 비즈니스 로직을 구현한 SubClassLogic 객체를 생성한다.
2. AbstractTemplate의 execute를 호출하면 추상 메서드 call을 SubClassLogic에서 override 된 내용이 실행된다.
3. SubClassLogic는 객체를 그냥 만들어사용할 수도 있고 익명 클래스로 만들 수도 있다.
정리하면 이런 구조가 된다.
비즈니스 로직은 변하지만, 실행 시간을 출력하는 로직은 변하지 않는다. 변경되는 비즈니스 로직은 AbstractTemplate을 상속받아 call()을 구현해주면 된다.
템플릿 메서드 패턴을 사용하면 변경되는 부분과 변경되지 않는 부분을 분리할 수 있지만, 상속을 사용해서 구현했다는 단점이 있다. 따라서 상속의 단점을 그대로 가져간다. 예를 들어 다음과 같은 문제가 있다.
- AbstractTemplate은 부모 클래스, SubClassLogic은 자식 클래스이지만 자식은 부모의 특성을 전혀 사용하지 않는다.
- 부모의 특성을 사용하지 않지만 부모 클래스의 수정으로 인해 자식 클래스도 영향을 받을 수 있다.
- 클래스는 다중 상속이 불가능하다. 추상 템플릿을 상속받으면 다른 클래스를 상속받을 수 없다.
- 논리적으로 부모 클래스와 상속의 관계가 맞는가? 인터페이스의 관계가 맞는가?