GOF 디자인 패턴의 행위 패턴
으로 분류되는 템플릿 메소드는,
GOF 패턴에서 아래와 같이 정의하고있다.
이보다 더 깔끔?한 정의는 못본것 같아서, 아래 정의만 봐도 충분할 듯 하다.
메소드 내에 알고리즘의 뼈대를 정의하고, 일부 단계를 하위클래스로 연기한다.
템플릿 메소드는 하위클래스가 알고리즘의 구조를 변경하지 않고, 특정 스텝을 재정의하도록 한다.
Defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithms structure.
템플릿 메소드 패턴의 클래스 다이어그램은 아래와 같다.
AbstractTemplate 클래스는 abstract class 로 정의하고, execute 메소드에 기본 골격이 되는 알고리즘이 들어간다.
이후, AbstractTemplate 클래스를 상속받아 call() 을 구현하고, execute() 메소드를 호출한다.
일상 생활에서, 밖에 나가는 상황으로 가정한다.
위에서 언급한 정의를 풀어보면
abstract class OutTemplate {
fun out() {
println("일어난다.")
readyProcess()
println("나간다.")
}
abstract fun readyProcess()
}
class WorkOutTemplate: OutTemplate() {
override fun readyProcess() {
println("대충 씻는다.")
println("대충 옷을 입는다.")
}
}
class PlayOutTemplate: OutTemplate() {
override fun readyProcess() {
println("노래를 부르며 씻는다.")
println("화장도 한다.")
println("퍼스널 컬러에 맞춰 옷을 꾸며입는다.")
}
}
class TemplateMethodTest {
@Test
fun outTest(){
WorkOutTemplate().out()
PlayOutTemplate().out()
}
}
일어난다.
대충 씻는다.
대충 옷을 입는다.
나간다.
일어난다.
노래를 부르며 씻는다.
화장도 한다.
퍼스널 컬러에 맞춰 옷을 꾸며입는다.
나간다.
step을 재정의
하기 위해 사용된다.위에 장단점을 보면, 장점보다 단점이 많음을 한눈에 알 수 있다.
이러한 이유 때문에, 템플릿 메소드 패턴과 비슷하지만 인터페이스를 사용하여 상속의 단점을 제거할 수 있는 전략 패턴
을 사용하기도 한다.
템플릿 메소드 패턴은, 부모 패턴에서 알고리즘의 뼈대를 구현하고
일부 변하는 step을 서브클래스에 구현하여 문제를 해결했다.
전략 패턴은 변하지 않는 부분(템플릿 역할)을 Context 라는 곳에 두고, 변하는 부분(서브클래스, step 구현 역할)을 Strategy 라는 인터페이스를 만들고 해당 인터페이스를 구현하도록 해서 문제를 해결한다.
즉, 템플릿 메소드는 상속을 통해 문제를 해결하는 반면
전략 패턴은 위임과 합성을 통해 문제를 해결한다.
아직은 내용이 어려울 수 있으니 사진과 코드로 확인하자.
사진 출처 : 인파님 블로그 - (전략(Strategy) 패턴 - 완벽 마스터하기)에 구조가 너무 잘 정리돼있어서 인용했다.
위 사진과 같이 전략 패턴은 소프트웨어 설계 원칙을 지킨다.
interface OutStrategy{
fun readyProcess()
}
class Context {
fun out(outStrategy: OutStrategy) {
println("일어난다.")
outStrategy.readyProcess()
println("나간다.")
}
}
class WorkOutStrategy: OutStrategy {
override fun readyProcess() {
println("대충 씻는다.")
println("대충 옷을 입는다.")
}
}
class PlayOutStrategy: OutStrategy {
override fun readyProcess() {
println("노래를 부르며 씻는다.")
println("화장도 한다.")
println("퍼스널 컬러에 맞춰 옷을 꾸며입는다.")
}
}
class StrategyTest {
@Test
fun outTest(){
val workOutStrategy = WorkOutStrategy()
val playOutStrategy = PlayOutStrategy()
val context = Context()
context.out(workOutStrategy)
context.out(playOutStrategy)
}
}
일어난다.
대충 씻는다.
대충 옷을 입는다.
나간다.
일어난다.
노래를 부르며 씻는다.
화장도 한다.
퍼스널 컬러에 맞춰 옷을 꾸며입는다.
나간다.
템플릿 콜백 패턴은 GOF 패턴에서 얘기하는 디자인 패턴이 아니라,
단지 스프링에서 위의 전략패턴을 템플릿 콜백 패턴이라고 정의한다.
전략 패턴의 Context -> 템플릿
Strategy -> 콜백
스프링에서는 JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate 처럼 다양한 템플릿 콜백 패턴이 사용된다.
스프링에서 이름에 XxxTemplate 가 있다면 템플릿 콜백 패턴으로 만들어져있다 생각하면 된다.
https://refactoring.guru/ko/design-patterns/template-method
https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4_%EA%B5%AC%EC%A1%B0
김영한 - 스프링 핵심 원리 고급편