====== the Definition of Template Method Pattern ======
템플릿 메소드 패턴의 정의는 다음과 같습니다.
템플릿 메소드 패턴에서는 메소드에서 알고리즘의 골격을 정의합니다.
알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다.
템플릿 메소드를 이용하면 알고리즘의 구조는 구대로 유지하면서 서브
클래스에서 특정단계를 재정의할 수 있습니다.
이 패턴은 간단하게 말하자면 알고리즘의 틀을 만들기 위한 것입니다. 틀(템플릿)이란 무엇을 뜻할까요?\\
그냥 메소드에 불과합니다. 조금 더 구체적으로 얘기하자면, 일련의 단계들로 알고리즘을 정의한 메소드입니다.\\
여러 단계 가운데 하나 이상이 추상 메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현됩니다.\\
이렇게 하면 서브클래스에서 일부분을 구현할 수 있도록 하면서도 알고리즘의 구조는 바꾸지 않아도 되도록 할 수 있습니다.\\
클래스 다이어그램을 한번 살펴봅시다.
{{:study:java:design_pattern:template_method.jpg|Template Method}}
{{keywords>Template Method Pattern}}
===== snippet of AbstractClass =====
이번에는 추상 클래스에 들어갈 수 있는 메소드의 형식에 대해 조금 더 자세하게 살펴봅시다.
abstract class AbstractClass{
final void templateMethod(){
primativeOperation1();
primativeOperation2();
concreteOperation();
}
abstract void primitiveOperation1();
abstract void primitiveOperation2();
final void concreteOperation(){
//concreteOperation() 코드
}
void hook() {}
}
여기서 구상단계는 추상 클래스내에서 정의됩니다. 이 메소드는 final로 선언되어 있기 때문에 서브 클래스에서 오버라이드 할 수 없겠죠.\\ 이 메소드는 템플릿 메소드에서 직접 호출할 수도 있고, 서브클래스에서 호출해서 사용할 수도 있습니다.\\
**%%hook()%%
**는 아무것도 하지 않지만, 유연성을 제공하도록 위해 정의 되었습니다. 이런 메소드는 "후크(hook)"라고 부릅니다. \\
서브클래스에서 오버라이드 할 수 있지만, 반드시 그래야 하는 건 아니죠. 이 메소드를 어떤 식으로 활용하는지는 밑에서 살표보도록
하겠습니다.
===== template method and hook =====
후크는 추상 클래스에서 선언되는 메소드긴 하지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드입니다.\\
이렇게 하면 서브클래스 입장에서는 다양한 위치에서 알고리즘에 끼어들 수 있습니다. 물론 그냥 무시하고 넘어갈수도 있죠.
후크는 다양한 용도로 쓰일 수 있습니다. 일단 한가지 사용법을 살펴볼까요? 나중에 몇 가지 다른 활용법도 알아보게 될 것입니다.
public abstract class CaffeineBeverageWithHook{
void prepareRecipe(){
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()){
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater(){
System.out.println("물 끓이는 중");
}
void pourInCup(){
System.out.println("컵에 따르는 중");
}
boolean customerWantsCondiments(){
return true;
}
}
여기서 후크 역할을 하는 메소드가 **%%customerWantsCondiments()%%**입니다. 이 메소드는 필요에 따라 서브클래스에서
오버라이드 할 수 있습니다.\\
후크를 사용하려면 서브클래스에서 오버라이드 해야 합니다.\\ 이 예에서는 **%%CaffeineBeverage%%**에서 알고리즘의 특정 부분을
처리할지 여부를 결정하기 위한 용도로 후크를 사용했습니다.\\ 음료에 첨가물을 추가할 지 여부를 결정하기 위한 메소드죠.
손님이 첨가물을 추가하고 싶어하는지 어떻게 알 수 있을까요? 뭐 별 수 없죠... 그냥 물어보는 수 밖에...
public class CoffeeWithBook extends CaffeineBeverageWithHook{
public void brew(){
System.out.println("필터로 커피를 우려내는 중");
}
public void addCondiments(){
System.out.println("우유와 설탕을 추가하는 중");
}
public boolean customerWantsCondiments(){
String answer =getUserInput();
if(answer.toLowerCase().startsWith("y")){
return true;
}else{
return false;
}
}
private String getUserInput(){
String answer = null;
System.out.println("커피에 우유와 설탕을 넣어드릴까요? (y/n) ");
BufferedReader in =new BufferedReader(new InputStreamReader(System.in));
try{
answer = in.readLine();
}catch(IOException ioe){
System.err.println("IO error")
}
if(answer == null){
return "no";
}
return answer;
}
}
===== sorting using template-method pattern =====
배열을 가지고 자주 하는 일 가운데 뭐가 있을까요? 정렬(sort)많이들 하시죠?
자바의 Arrays 클래스에는 정렬할 때 쓸 수 있는 편리한 템플릿 메소드가 있습니다. 그 메소드가 어떤 식으로 작동하는지 살펴봅시다.
public static void sort(Object[] a){
Object aux[] = (Object[])a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
이 %%sort()%%메소드는 배열의 복사본을 만든 다음 %%mergeSort()%%를 호출할때 대상 배열로 전달해 주기 위한 보조 메소드입니다.\\ %%mergeSort()%%를 호출할 때는 배열의 길이와 첫번째 원소의 위치(0)도 알려줘야 합니다.
private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off){
for(int i=low; ilow && ((Comparable)dest[j-1]).compareTo((Comparable)dest[j])>0; j--){
swap(dest, j, j-1);
}
}
return;
}
%%mergeSort()%%메소드에는 정렬 알고리즘이 들어있으며, %%compareTo()%%메소드에 의해 결과가 결정됩니다. \\
정렬이 실제로 어떤 식으로 진행되는지 알고 싶다면 썬에서 만든 소스 코드를 확인해 보세요.\\
%%swap%%메소드는 %%Arrays%% 클래스에 이미 정의되어 있는 구상 메소드입니다.
===== implementation of Comparable interface =====
이제 정렬 알고리즘을 완성하기 위해 **compareTo()**메소드를 구현해보겠습니다.
public class Duck implements Comparable{
String name;
int weight;
public Duck(String name, int weight){
this.name = name;
this.weight = weight;
}
public String toString(){
return name + ", weight: " + weight;
}
public int compareTo(Object object){
Duck otherDuck = (Duck) object;
if(this.weight < otherDuck.weight){
return -1;
}else if(this.weight == otherDuck.weight){
return 0;
}else{
return 1;
}
}
}