본문 바로가기

IT Book Summary/디자인패턴

템플릿 메소드 패턴 The Template Method Pattern: Encapsulating Algorithms

 

커피 및 홍차 클래스 만들기

커피 만드는 것과 홍차 만드는것의 공통부분을 추상화 하자.

 

동일한 알고리즘 추상화.

  1. 물을 끓인다.
  2. 뜨거운 물을 이용해 커피 또는 홍차를 우려냄.
  3. 만들어진 음료를 컵에 따름.
  4. 각 음료에 맞는 첨가물을 추가한다.

 

 

prepareRecipe() 추상화하기

 

1. 비슷한 행위를 같은이름으로 통일해서 새로운 메소드를 만듬.

// Coffee
void prepareRecipe() {
  boilWater();
  brewCoffeeGrinds();
  purInCup();
  addSugarAndMilk();
}

// Tea
void prepareRecipe() {
  boilWater();
  steepTeaBag();
  purInCup();
  addLemon();
}

// 메소드 통일
void prepareRecipe() {
  boilWater();
  brew();
  purInCup();
  addCondiments();
}

 

2. CaffeineBeverage 수퍼클래스

public abstract class CaffeineBeverage {

  final void prepareRecipe() {
    boilWater();
    brew();
    pourInCup();
    addCondiments();
  }
  
  // 서로 다른방식으로 처리되는 과정은 추상 메소드로 선언
  abstract void brew();
  
  abstract void addCondiments();
  
  void boilWater() {
    System.out.println("물 끓이는 중");
  }
  
  void pourInCup() {
    System.out.println("컵에 따르는 중");
  }
}

 

3. Coffee 와 Tea 클래스

public class Tea extends CaffeinBeverage {
  public void brew() {
    System.out.println("차 우려내는 중");
  }
  
  public void addCondiments() {
    System.out.println("레몬 추가");
  }
}


public class Coffee extends CaffeinBeverage {
  public void brew() {
    System.out.println("필터로 커피 우려내는 중");
  }
  
  public void addCondiments() {
    System.out.println("설탕과 우유 추가");
  }
}

 

 

 

템플릿 메소드 패턴에서는 메소드에서 알고리즘의 골격을 정의.
알고리즘 여러 단계 중 일부는 서브클래스에서 구현할 수 있음.
템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브 클래스에서 특정 단계를 재정의 가능.

 

 

템플릿 메소드와 후크 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;
  }
}

public class BeverageTestDrive {
  public static void main(String[] args) {
    TeaWithHook teaHook = new TeaWithHook();
    CoffeeWithHook coffeeHook = new CoffeeWithHook();
    
    System.out.println("\n차 준비중");
    teaHook.prepareRecipe();
    
    System.out.println("\n차 준비중");
    coffeeHook.prepareRecipe();
  }
}

 

헐리우드 원칙과 템플릿 메소드 패턴

 

헐리우드 원칙을 활용하면 '의존성 부패 dependency rot' (의존성이 복잡하게 꼬여있는 것)을 방지

저수준 구성요소에서는 고수준 구성요소를 직접 호출 불가. 

언제 어떤식으로 쓰이는지는 고수준 구성요소에 의해 결정.

 

템플릿 메소드 패턴 알고리즘의 일부단계를 구현하는 것을 서브 클래스에서 처리
스트래티지 패턴 바꿔쓸 수 있는 행동을 캡슐화하고, 어떤 행동을 사용할지는 서브클래스에 맡김.
팩토리 메소드 패턴 어떤 구상 클래스를 생성할지를 서브클레스에서 결정.

 

야생의 템플릿 메소드. 변형된 패턴 알아보기.

 

템플릿 메소드를 이용해서 정렬.

Java Arrays 클래스의 sort.

 

// 배열의 복사본을 만든 후 mergeSort() 호출.
public static void sort(Object[] a) {
  Object aux[] = (Object[])a.clone();
  mergeSort(aux, a, 0, a.length, 0);
}

// 정렬 알고리즘. compareTo() 메소드에 의해 결과 결정.
private static void mergeSort(Object src[], Object dest[], int low, int high, int off) {
  for(int i=low; j>low && 
        ((Comparable)dest[j-1]).compareTo((Comparable)dest[j])>0; j--)
  {
    swap(dest, j, j-1);
  }
  return;
}

 

CompareTo 메소드의 비교하는 방법을 구현해야 함. 두 객체를 비교해서 판단결과를 리턴.

Comparable 인터페이스 의 메소드.

 

Duck 정렬

public class Duck implements Comparable {
  Stirng name;
  int weight;
  
  // 오리의 이름과 체중이 부여.
  public Duck(String name, int weight) {
    this.name = name;
    this.weight = weight;
  }
  
  public Sring toString() {
    return name + ", 체중: " +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;
    }
  }
}

public class DuckSortTestDrive {
  public static void main(String[] args) {
    Duck[] ducks = {
      new Duck("Daffy", 8),
      new Duck("Dewey", 2),
      new Duck("Howard", 7),
      new Duck("Louie", 2),
      new Duck("Donald", 10),
      new Duck("Huey", 2),
    };

    Arrays.sort(ducks);
    
  }
}

 

Arrays 의 sort() 메소드는 어떤 배열에서도 정렬 기능을 사용할 수 있어야 함.

그래서 정적메소드를 정의한 다음, 알고리즘에서 대소 비교 하는 부분은 정렬될 객체에서 구현하도록 함.

sort() 메소드 구현 자체를 템플릿 메소드 패턴의 기본을 따른다.

 

스트래티지 패턴에서는 구성을 위해 사용하는 (객체 레퍼런스에 의해 참조되는)클래스에서 알고리즘을 완전히 구현.

 

자바 API 중 java.io의 InputStream의 read() 메소드 또한 서브클래스에서 구현해주어야 하고 

read(byte b[], int off, in len) 템플릿 메소드로 쓰임.

 

애플릿에서도 다양한 후크를 통해 원하는 작업을 처리한다. 후크메소드는 꼭 구현해야하는건 아님.

 

템플릿 메소드 패턴 스트래티지 패턴
- 알고리즘의 개요를 정의함.
- 실제 작업중 일부는 서브클래스에서 처리.
- 알고리즘의 각 단계마다 다른 구현을 사용하면서 알고리즘 구조 자체를 그대로 유지.
- 수퍼클래스에 있는 중복되는 코드는 서브클래스에서 공유.
- 알고리즘이 다 똑같고 한줄만 다르면 더 효율적.
- 일련의 알고리즘군을 정의하고 그 알고리즘을 서로 바꿔 쓸수 있게 해줌.
- 각 알고리즘은 캡슐화 되어있어 클라이언트가 쉽게 다른 알고리즘 사용.
- 클라이언트가 객체 구성을 통해 알고리즘 구현을 선택하게 함.
- 객체 구성으로 유연하게 사용 가능.