目次
the Definition of 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; i<high; i++){ for(int j=i; j>low && ((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; } } }