본문 바로가기

IT/헤드퍼스트 디자인패턴

[디자인패턴] 템플릿 메소드 패턴

안녕하세요 남갯입니다.


오늘은 템플릿 메소드 패턴에 대해 포스팅 해보려고 합니다.



사건의 발단


커피와 차에 공통점이 있는데 매우 비슷한 방법으로 만들어진다고 합니다.


- 커피만드는법

1. 물을 끓인다

2. 물에 커피를 우려낸다

3. 커피를 컵에 딸느다.

4. 설탕과 우유를 추가한다.


커피를 클래스화 해보겠습니다.


class Coffee{

fun prepareRecipe(){
boilWater()
brewCoffeeGrinds()
pourInCup()
addSugarAndMilk()
}

fun boilWater(){
println("물 끓이는중")
}
fun brewCoffeeGrinds(){
println("필터를 통해서 커피를 우려내는 중")
}
fun pourInCup(){
println("컵에 따르는 중")
}
fun addSugarAndMilk(){
println("설탕과 우유를 추가하는중")
}
}

커피를 위와같이 만들 수 있고



-홍차만드는법

1. 물을 끓인다.

2. 물에 차를 우려낸다.

3. 차를 컵에 따른다.

4. 레몬을 추가한다.


홍차 클래스를 만들어 보겠습니다.


class Tea{

fun prepareRecipe(){
boilWater()
steepTeaBag()
pourInCup()
addLemon()
}

fun boilWater(){
println("물 끓이는중")
}
fun steepTeaBag(){
println("차를 우려내는중")
}
fun addLemon(){
println("레몬을 추가하는 중")
}
fun pourInCup(){
println("컵에 따르는중")
}
}


위와 같은 형태로 만들 수 있습니다.


위에서 두개의 클래스를 보면 알 수 있듯이 거의 똑같으니까 공통적인 부분을 추상화시켜서 클래스를 만드려고 합니다.


위의 공통적인 부분을 통해 만든다고 하면


abstract class CaffineBeverage{
fun prepareRecipe(){}
fun boilWater(){}
fun pourInCup(){}
}


이렇게 만들수 있다고 생각하지만 더 자세히 보게되면 두개의 공통점은 또 있습니다.


1. 물을끓인다

2. 뜨거운물을 이용해 차or커피를 우려낸다.

3. 만들어진 음료를 컵에 따른다.

4. 각 음료에 맞는 첨가물을 추가한다.



그래서 우리는 행동자체는 비슷한 두개의 클래스를

brew()와 addCondiments() 를 통해 추상화시켜도 됩니다.


abstract class CaffineBeverage {
fun prepareRecipe() {
boilWater()
brew()
pourInCup()
addCondiment()
}
abstract fun brew()
abstract fun addCondiment()
fun boilWater() = println("물 끓이는중")
fun pourInCup() = println("컵에 따르는중")
}

이런 추상클래스를 만든후에 공통적인 부분은 구현을 하고 

추상적인 부분은 상속받은 클래스에서 구현하도록 만듭니다.


그 후에


class Coffee : CaffineBeverage() {
override fun brew() = println("필터를 통해서 커피를 우려내는 중")
override fun addCondiment() = println("설탕과 우유를 추가하는중")
}
class Tea : CaffineBeverage() {
override fun brew() = println("차를 우려내는중")
override fun addCondiment() = println("레몬을 추가하는 중")
}

다른 부분을 커피와 티가 구현해서 동작하도록 만들면 됩니다.


위와 같이 기본적인 클래스에 대한 템플릿을 만들어 알고리즘을 구성하고 중간단계중 한개 이상의 단계를 서브클래스에서 구현하여 제공하는 것을 말합니다.


fun main(){
val tea = Tea()
tea.prepareRecipe()
}
실제의 알고리즘을 tea.prepareRecipe을 통해 간단하게 구현이 가능합니다.


템플릿 메소드의 정의
메소드의 알고리즘의 골격을 정의합니다. 여러단계의 일부는 서브클래스에서 구현가능하고 알고리즘을 그대로 유지하면서 서브클래스에서 특정단계를 재정의 가능합니다. 간단하게 말하자면 알고리즘의 틀을 만들기 위해 만들어 진것입니다.


후크

템플릿메소드에는 후크라는 것을 통해 다양한 위치에서 알고리즘을 끼어 들 수 있습니다.

후크는 아무런 코드가 들어가 있지 않은 메소드로, 서브클래스에서 구현하도록 하는 메소드입니다.


abstract class CaffineBeverage {
fun prepareRecipe() {
boilWater()
brew()
pourInCup()
if (customerWantsCondiemnts())
addCondiment()
}

abstract fun brew()
abstract fun addCondiment()
fun boilWater() = println("물 끓이는중")
fun pourInCup() = println("컵에 따르는중")
fun customerWantsCondiemnts(): Boolean = true
}


이렇게 후크메소드를 만든 후에



class Tea : CaffineBeverage() {
override fun brew() = println("차를 우려내는중")
override fun addCondiment() = println("레몬을 추가하는 중")
override fun customerWantsCondiemnts(): Boolean {
val answer = getUserName()
if(answer.toLowerCase().startsWith("y")){
return true
}else{
return false
}

}
fun getUserName() = "y"
}

훅 메소드를 통해 서브클래스에서 구현함으로서 


실제 소비자가 첨가물을 원하는지를 확인 하고 알고리즘을 구현한 부분에 따라 첨가물을 넣을지 말지 구성하는것이 가능합니다.


이렇게 서브클래스에서 구현하든 말든 하도록 하는경우 후크를 통해 결정을 내리는 기능을 부여 가능합니다.



디자인원칙 헐리우드

먼저 연락하지 마세요 저희가 연락드리겠습니다.


의존성 구성요소들이 복잡하게 의존되어 있는것을 의존성 부패라고 부르는데, 할리우드 원칙을 통해 저수준 구성요소에서 시스템을 접속 할 수는 있지만 언제 어떤 식으로 그 구성요소들을 사용할 지는 고수준 구성요소에서 결정하게 하는것입니다. 즉 고수준 구성요소는 저수준 구성요소에게 먼저 연락하지 마세요 제가 먼저 연락드리겠습니다. 라는것과 같은 의미입니다.


이 디자인 원칙을 가진 헐리우드 원칙을 템플릿 메소드 패턴을 쓰게되면 헐리우드 원칙을 지키게 됩니다.