Template Method Pattern
작업의 일부분을 캡슐화하여 전체 일을 수행하는 구조는 그대로 유지하면서 특정 부분을 바꾸는 패턴
메소드내에 알고리즘 골격을 정의한다.
variant step의 구현을 subclass들에게 미룬다.(defer)
하위 클래스는 알고리즘 구조를 변경하지 않는다.
variant step을 자신의 방법으로 구현한다.
The AbstarctClass 는 템플릿 메소드를 포함한다.
templateMethod()
- templateMethod() 에서 primitiveOperation 들이 사용된다.
- concrete invariant operations
- abstract variant operations
- hook (default or null operation)
primitiveOperation1 ,primitiveOperation2
알고리즘 골격은 변경하지 않고
variant operation을 자신의 방법으로 구현함.
abstract class AbstractClass
{
// The template method defines the sequence of steps, each represented by a method
final void templateMethod()
{
primitiveOperation1();
primitiveOperation2();
concreteOperation();
}
// concrete subclasses에서 반드시 구현
abstract void primitiveOperation1();
abstract void primitiveOperation2();
// 미리 구현돼 있는 메소드
void concreteOperation() {
// implementation here
}
}
final void templateMethod()
final로 선언해서 subclasses로 부터 보호한다.
Hook
- hook 은 abstract class 에 정의하는 클래스이다.
- hook 은 비어있거나 default 구현을 갖는 template method 내의 메소드에 주어진다.
- 알고리즘 기능을 변경하거나 추가하기 위해 사용한다.
- Use hooks when that part of the algorithm(templateMethod) is optional
- With hooks, a subclass may choose to implement that hook, but it doesn’t have to.
Optional step
abstract class AbstractClass
{
final void templateMethod()
{
operation1();
operation2();
if (hook()) {
optionalOperation();
}
}
abstract void operation1();
abstract void operation2();
abstract void optionalOperation();
boolean hook() { return true; }
}
hook() : Subclasses are free to override these but don't have to. -->> A concrete method, but it does noting
Augmented step
abstract class AbstractClass
{
final void templateMethod()
{ // 골격을 미리 정의
preHook(); // 사전 연산
anOperation(); // 어떤 연산
postHook(); // 사후 연산
...
}
// 추가 연산
// 필요시 서브클래스에서 재정의
void preHook() { /* no op */ }
void postHook() { /* no op */ }
}
Hooking covers a range of techniques used to alter or augment the behavior of modules
by intercepting function calls or messages/events passed between modules.
Code that handles intercepted function calls or messages/events is called a hook.
독립적으로 컴파일 될 수 있는 구성단위
활용 예로
- public void init(), start(), stop(), destroy() // android studio 생명주기 관리 느낌
예제)
public abstract class CaffeineBeverageWithHook
{
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) { // hook
addCondiments();
}
}
abstract void brew();
abstract void addCondiments(); // subclasses 에서 구현,
void boilWater() {
System.out.println(“Boiling water”);
}
void pourInCup() {
System.out.println(“Pouring into cup”);
}
boolean customerWantsCondiments() {
return true;
}
}
public class CoffeeWithHook extends CaffeineBeverageWithHook
{
public void brew() {
System.out.println(“Dripping Coffee through filter”);
}
public void addCondiments() { // 훅에 따라 실행된다.
System.out.println(“Adding Sugar and Milk”);
}
public boolean customerWantsCondiments() { // 사용자에 입력에 따라 hook 을 쓴다.
String answer = getUserInput();
if (answer.toLowerCase().startsWith(“y”)) {
return true;
} else {
return false;
}
}
private String getUserInput() { // 이너 함수로 사용
String answer = null;
System.out.print(“Would you like milk and sugar with your coffee (y/n)? “);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
answer = in.readLine();
} catch (IOException ioe) {
System.err.println(“IO error trying to read your answer”);
}
if (answer == null) {
return “no”;
}
return answer;
}
}
Sorting with Template Method
Template method pattern을 적용하면 비교하려는 모든 객체는
Arrays의 subtype이 되어야 함.
필요할때마다, AbstractArrays를 상속
Account 도 상속받은 구현객체
다중 상속해야돼서 안된다.
NONSENSE !!
compareTo()는 배열의 타입에 따라 달라진다.
설계 원칙 위배
SRP 참고 비슷한 개념(하나의 책임)
Separation of responsibilities
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;
}
머지 소트 알고리즘 (template method 에서
compateTo 메소드를 부분적으로 사용한다.
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 + “ weighs “ + 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 { // this.weight > otherDuck.weight
return 1;
}
}
}
- 알고리즘을 메소드로 정의하고 하위 클래스에서 알고리즘 구조의 변경 없이 알고리즘 재정의
- 공통적인 작업 흐름은 추상 클래스에서 정의하고 나머지 구현들은 각각의 서브 클래스에서 담당
- 전체적으로는 동일하면서 부분적으로는 다른 메소드의 코드 중복을 최소화할 때 유용
댓글