====== 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; } } }