Template Method Pattern 템플릿 메소드 패턴
개념
템플릿 메소드 패턴이란 메소드에서 알고리즘의 골격을 정의한다. 그리고 알고리즘의 여러 단계 중 일부는 서브 클래스에서 처리한다.
예시
커피 만드는 법과 홍차 만드는 법은 같은 알고리즘을 가지고 있다.
커피 만드는 법 | 홍차 만드는 법 |
1) 물을 끓인다. 2) 끓는 물에 커피를 우려낸다. 3) 커피를 컵에 따른다. 4) 설탕과 우유를 추가한다. |
1) 물을 끓인다. 2) 끓는 물에 차를 우려낸다. 3) 차를 컵에 따른다. 4) 레몬을 추가한다. |
(1)번과 (4)번에 어떤 재료를 사용하느냐의 차이만 있을 뿐 큰 알고리즘은 같다. 이럴 경우 (1)~(4)를 하나의 알고리즘으로 메소드에 넣고, 차이점이 있는 (2)번과 (4)번만 각각의 서브 클래스에서 처리하도록 한다.
예시 코드
커피와 홍차 모두 카페인이 들어있기 때문에 CaffeineBevarage라는 클래스로 만들어보겠다.
템플릿 메소드 패턴의 클래스
// java
// Template Method Pattern
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("물 끓이는 중");
}
void pourInCup() {
System.out.println("컵에 따르는 중");
}
}
커피와 홍차를 만들 때 똑같은 prepareRecipe() 메소드를 사용한다. 서브 클래스에서 이 메소드를 오버라이드해서 아무렇게나 음료를 만들지 못하도록 final로 선언했다.
// 홍차, 커피
public class Tea extends CaffeinBeverage {
public void brew() {
System.out.println("차를 우려내는 중");
}
public void addCondiments() {
System.out.println("레몬을 추가하는 중");
}
}
public class Coffee extends CaffeinBeverage {
public void brew() {
System.out.println("필터로 커피를 우려내는 중");
}
public void addCondiments() {
System.out.println("설탕과 커피를 추가하는 중");
}
}
두번째와 네번째 단계인 brew와 addCondiments는 추상 메소드로 선언하여 서브 클래스에서 정의하여 사용하도록 했다.
구조
추상 클래스는 템플릿 메소드를 포함하고 있고, 그 템플릿 메소드에서 알고리즘의 골격을 정의한다.
알고리즘의 여러 단계 중 일부는 서브 클래스에서 정의하여 사용할 수 있도록 추상화되어있다.
유의점
템플리 메소드 패턴을 사용할 때 유의할 부분은 추상 메소드가 많아지지 않도록 해야하는 것이다. 왜냐하면 모든 추상 메소드를 서브 클래스에서 채워줘야하기 때문에 갯수가 많을 경우 구현하기 번거로워질 수 있다.
추상 메소드의 갯수를 줄이기 위해서는 알고리즘의 단계를 너무 잘게 쪼개지 않고 큼지막하게 나누는 것도 방법이 될 수 있다.
Template Method & Hook 템플릿 메소드와 후크
후크는 수퍼클래스에서 만들어놓은 빈 메소드이다. 필요에 따라 서브클래스에서 오버라이드하여 사용할 수 있다.
후크 사용 예시
시럽 추가 유무, 나는 커피에는 바닐라 시럽을 넣어 마시고 홍차는 시럽을 추가하지 않는다.
따라서 같은 알고리즘으로 음료를 만들고 마지막에 커피에만 시럽을 넣는다.
코드
// 후크 메소드 - addSyrup()
public abstract class CaffeineBeverageWithHook {
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addSyrup(); // 후크 메소드
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("물 끓이는 중");
};
void pourInCup() {
System.out.println("컵에 따르는 중");
};
void addSyrup() {
// 빈 메소드
// 서브 클래스에서 오버라이드하여 사용.
};
}
커피의 경우 수퍼클래스에서 선언된 addSyrup을 재정의하여 바닐라 시럽을 추가한다.
public class CoffeWithHook extends CaffeinBeverageWithHook {
public void brew() {
...
};
public void addCondimnets() {
...
};
public void addSyrup(){ // 후크를 오버라이드해서 원하는 기능 집어 넣음
System.out.println("바닐라 시럽 넣는 중");
};
}
홍차는 위와 동일한 코드에서 addSyrup메소드만 정의하지 않으면 된다. 수퍼 클래스에서 비어있는 메소드이기 때문에 아무것도 처리하지 않는다.
장점
이와 같이 후크를 사용할 경우, 서브 클래스 입장에서는 다양한 위치에서 알고리즘에 끼어들 수 있다.
즉, 알고리즘의 특정 부분이 선택적으로만 적용해야할 경우 사용하면 편리하다.
검색 페이지에서
시간 검색, 이벤트 검색, 텍스트 검색 등 여러 검색 페이지가 있는데
여기서 검색하려는 필터 채널과 날짜와시간은 같이 쓰고 시간, 이벤트,텍스트만 따로 씀
알고리즘은 검색에서 독점하고 있음. 그리고 채널 선택, 날짜와 시간 선택, 검색 버튼, 초기화 버튼은 검색 클래스에서 수행하고,
특수 필터 선택만 하위 서브 클래스에서 실행하도록 하고 있음