[디자인패턴 수업 13주차 1차시] Template Method Pattern

Jin Hur·2021년 11월 23일
0
post-custom-banner

reference: https://astrod.github.io/[Head%20First]%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EB%A9%94%EC%86%8C%EB%93%9C%20%ED%8C%A8%ED%84%B4/

정의

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

다시 말해 이 패턴은 알고리즘의 틀을 만들기 위해 사용한다. 틀이란 일련의 단계들로 알고리즘을 정의한 메서드이다. 여러 단계 가운데 하나 이상이 추상 메서드로 정의되며, 그 추상 메서드는 서브클래스에서 구현된다. 이렇게 하면 서브클래스에서 일부분을 구현할 수 있으면서도, 알고리즘의 구조는 바꾸지 않을 수 있다.


바리스타 예시

<카페의 바리스타 메뉴얼>
커피 만드는 법
1) 물을 끓인다.
2) 커피를 우린다.
3) 컵에 따른다.
4) 설탕과 우유를 추가한다.

홍차 만드는 법
1) 물을 끓인다.
2) 차를 우린다.
3) 컵에 따른다.
4) 레몬을 추가한다.

before applying pattern

// 커피 클래스
public class Coffee {
    void prepareRecipe(){
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    public void boilWater(){
        System.out.println("물을 끓입니다.");
    }
    public void brewCoffeeGrinds(){
        System.out.println("커피를 우립니다.");
    }
    public void pourInCup(){
        System.out.println("컵에 따릅니다.");
    }
    public void addSugarAndMilk(){
        System.out.println("설탕과 우유를 추가합니다.");
    }
}
// 홍차 클래스
public class Tea {
    void prepareRecipe(){
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }

    public void boilWater() {
        System.out.println("물을 끓입니다.");
    }

    public void steepTeaBag() {
        System.out.println("차를 우려냅니다.");
    }

    public void pourInCup() {
        System.out.println("컵에 따릅니다.");
    }

    public void addLemon() {
        System.out.println("레몬을 추가합니다.");
    }
}

코드 중복 제거 적용

추상클래스를 추가하여, boilwater, pourInCup 같은 중복되는 코드를 상위로 올린다.

public abstract class CaffeineBeverage {
    // 공통적인 부분은 추상클래스로 추출.
    
    abstract void prepareRecipe();

    public void boilWater(){
        System.out.println("물을 끓입니다.");
    }
    public void pourInCup(){
        System.out.println("컵에 따릅니다.");
    }
}
public class Coffee extends CaffeineBeverage{
    void prepareRecipe(){
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    // 코드 중복 제거
    // public void boilWater(){
    //    System.out.println("물을 끓입니다.");
    //}
    //public void pourInCup(){
    //    System.out.println("컵에 따릅니다.");
    //}

    public void brewCoffeeGrinds(){
        System.out.println("커피를 우립니다.");
    }
    public void addSugarAndMilk(){
        System.out.println("설탕과 우유를 추가합니다.");
    }
}
public class Tea extends CaffeineBeverage{
    void prepareRecipe(){
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }

    // 코드 중복 제거
    //public void boilWater() {
    //    System.out.println("물을 끓입니다.");
    //}
    //public void pourInCup() {
    //    System.out.println("컵에 따릅니다.");
    //}

    public void steepTeaBag() {
        System.out.println("차를 우려냅니다.");
    }
    public void addLemon() {
        System.out.println("레몬을 추가합니다.");
    }
}

Template Method 패턴 적용

  1. CaffeineBeverage 클래스에서 작업을 처리하여 알고리즘이 하나로 통일되도록 한다.
  2. 알고리즘이 한데 모여있어 그 부분만 고칠 수 있도록 한다.
  3. 카페인 음료를 추가할 때 CaffeineBeverage 클래스를 extends 한 후, 몇 가지 메서드만 추가하도록 한다.
    결국 CaffeineBeverage 클래스에 알고리즘에 대한 지식이 집중되고, 일부 구현만 서브클래스에 구현하도록 한다.
public abstract class CaffeineBeverage {
    // 공통적인 부분은 추상클래스로 추출.

    // 새로 생긴 의문점 -> prepareRecipe() 메서드도 추상화시킬 수 있지 않을까?
    // abstract void prepareRecipe();
    // template method 패턴 적용하기

    /**
     * 템플릿 메서드. 어떤 알고리즘에 대한 템플릿 역할을 한다.
     * 이 경우에는 카페인이 들어있는 음료를 만들기 위한 알고리즘의 템플릿이다.
     * 1) 탬플릿 내에서 각 단계는 메서드로 표현된다.
     * 2) 어떤 메서드는 이 클래스 내에서 구현된다. 어떤 서브클래스든 동일한 작동을 보이는 기능은 추상 클래스에서 정의.
     * 3) 어떤 메서드는 서브클래스에서 구현된다. 서브클래스에서 구현할 메서드는 abstract로 클래스 내부에서 선언.
     */
    final void prepareRecipe(){ // 상속받는 클래스에서 순서를 변경할 수 없게 final로 선언
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew();
    abstract void addCondiments();
    
    public void boilWater(){
        System.out.println("물을 끓입니다.");
    }
    public void pourInCup(){
        System.out.println("컵에 따릅니다.");
    }
}
public class Coffee extends CaffeineBeverage {
    @Override
    public void brew(){
        System.out.println("커피를 우립니다.");
    }
    @Override
    void addCondiments() {
        System.out.println("설탕과 우유를 추가합니다.");
    }
}
public class Tea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("차를 우려냅니다.");
    }
    @Override
    void addCondiments() {
        System.out.println("레몬을 추가합니다.");
    }
}
post-custom-banner

0개의 댓글