文書の過去の版を表示しています。
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{ }catch(IOException ioe){ System.err.println("IO error") } if(answer == null){ return "no"; } return answer; } }