관리 메뉴

SW

[개발서적] 헤드퍼스트 디자인 패턴 Ch8. 템플릿 메소드 패턴 본문

카테고리 없음

[개발서적] 헤드퍼스트 디자인 패턴 Ch8. 템플릿 메소드 패턴

SWKo 2024. 8. 30. 00:08

템플릿 메소드

템플릿 메소드는 알고리즘의 각 단계를 정의하며, 서브클래스에서 일부 단계를 구현할 수 있도록 유도합니다.

 

추상 클래스 - CaffeineBeverage

abstract class CaffeineBeverage {
    // 템플릿 메서드
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    abstract void brew();

    abstract void addCondiments();

    void boilWater() {
        System.out.println("물을 끓이는 중");
    }

    void pourInCup() {
        System.out.println("컵에 따르는 중");
    }

    // Hook - 서브클래스에서 필요시 오버라이드
    boolean customerWantsCondiments() {
        return true;
    }
}


Coffee 클래스

import java.util.Scanner;

public class Coffee extends CaffeineBeverage {

    @Override
    void brew() {
        System.out.println("필터를 통해 커피를 우려내는 중");
    }

    @Override
    void addCondiments() {
        System.out.println("설탕과 우유를 추가하는 중");
    }

    @Override
    boolean customerWantsCondiments() {
        String answer = getUserInput("커피에 설탕과 우유를 넣어 드릴까요? (y/n): ");
        return answer.toLowerCase().startsWith("y");
    }

    private String getUserInput(String prompt) {
        System.out.print(prompt);
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        return input;
    }
}

 

Tea 클래스

import java.util.Scanner;

public class Tea extends CaffeineBeverage {

    @Override
    void brew() {
        System.out.println("찻잎을 우려내는 중");
    }

    @Override
    void addCondiments() {
        System.out.println("레몬을 추가하는 중");
    }

    @Override
    boolean customerWantsCondiments() {
        String answer = getUserInput("차에 레몬을 넣어 드릴까요? (y/n): ");
        return answer.toLowerCase().startsWith("y");
    }

    private String getUserInput(String prompt) {
        System.out.print(prompt);
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        return input;
    }
}

 

메인 클래스

public class BeverageTestDrive {
    public static void main(String[] args) {
        CaffeineBeverage tea = new Tea();
        CaffeineBeverage coffee = new Coffee();

        System.out.println("\n차 준비 중:");
        tea.prepareRecipe();

        System.out.println("\n커피 준비 중:");
        coffee.prepareRecipe();
    }
}

 

 

  1. CaffeineBeverage 클래스:
    • prepareRecipe() 메서드는 알고리즘의 골격을 정의하는 템플릿 메서드입니다. 이 메서드는 final로 선언되어 서브클래스에서 오버라이드할 수 없습니다.
    • brew()와 addCondiments()는 추상 메서드로, 서브클래스에서 구체적으로 구현해야 합니다.
    • customerWantsCondiments()는 후크(Hook) 메서드로, 기본적으로 true를 반환하며, 서브클래스에서 오버라이드하여 사용자의 입력에 따라 다른 행동을 하도록 할 수 있습니다. 후크(Hook)는 추상 클래스에서 선언되지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드입니다. 이러면 서브클래스는 다양한 위치에서 알고리즘에 끼어들 수 있습니다.
  2. Coffee 클래스:
    • brew() 메서드에서는 커피를 우려내는 과정을 정의합니다.
    • addCondiments() 메서드에서는 설탕과 우유를 추가하는 과정을 정의합니다.
    • customerWantsCondiments() 후크 메서드를 오버라이드하여, 사용자가 설탕과 우유를 원하는지 물어보고 그에 따라 행동을 결정합니다.
  3. Tea 클래스:
    • brew() 메서드에서는 차를 우려내는 과정을 정의합니다.
    • addCondiments() 메서드에서는 레몬을 추가하는 과정을 정의합니다.
    • customerWantsCondiments() 후크 메서드를 오버라이드하여, 사용자가 레몬을 원하는지 물어보고 그에 따라 행동을 결정합니다.
  4. BeverageTestDrive 클래스:
    • Tea와 Coffee 객체를 생성하고, 각각의 prepareRecipe() 메서드를 호출하여 음료를 준비하는 전체 과정을 실행합니다.

 

템플릿 메서드 패턴은 알고리즘의 뼈대를 정의하고, 일부 단계를 서브클래스에서 재정의할 수 있게 하는 패턴입니다. 이 패턴을 통해 코드를 재사용하고, 알고리즘의 구조를 변경하지 않으면서 세부 구현을 다르게 할 수 있습니다.

주요 개념

  1. 템플릿 메서드(Template Method):
    • 상위 클래스에 정의된 메서드로, 알고리즘의 전체 구조를 결정합니다.
    • 이 메서드는 일련의 단계로 이루어져 있으며, 각 단계는 메서드 호출로 표현됩니다.
    • 일부 단계는 서브클래스에서 구현해야 하는 추상 메서드로 정의됩니다.
  2. 추상 메서드(Abstract Method):
    • 템플릿 메서드 내에서 호출되며, 서브클래스에서 구체적으로 구현해야 합니다.
    • 예: brew()와 addCondiments() 메서드는 서브클래스에서 구체적으로 정의해야 하는 단계입니다.
  3. 후크(Hook):
    • 후크는 선택적으로 오버라이드할 수 있는 메서드입니다. 기본 구현을 제공하지만, 서브클래스에서 필요에 따라 변경할 수 있습니다.
    • 후크를 통해 알고리즘의 특정 단계를 실행할지 말지를 결정하거나, 기본 동작을 변경할 수 있습니다.
    • 예: customerWantsCondiments()는 후크로 사용자가 추가 재료를 원하는지 여부를 확인할 수 있게 합니다.
  4. 상속을 통한 확장:
    • 템플릿 메서드 패턴은 상속을 통해 기능을 확장합니다. 상위 클래스에 정의된 템플릿 메서드를 그대로 사용하면서, 하위 클래스에서 구체적인 동작을 변경할 수 있습니다.

 

템플릿 메서드 패턴의 장점

  1. 코드 재사용성: 알고리즘의 공통 부분을 상위 클래스에서 정의하여 코드 중복을 줄일 수 있습니다.
  2. 알고리즘의 안정성 유지: 템플릿 메서드는 상위 클래스에서 정의되어 알고리즘의 기본 구조를 변경하지 않으면서, 서브클래스에서 세부 구현을 다르게 할 수 있습니다.
  3. 유연성: 후크 메서드를 통해 서브클래스에서 알고리즘의 동작을 필요에 따라 변경할 수 있습니다.
  4. 확장성: 새로운 서브클래스를 추가함으로써 알고리즘의 새로운 변형을 쉽게 구현할 수 있습니다.

템플릿 메서드 패턴 사용 시 고려 사항

  • 상속을 통한 확장이라는 구조적 특성 때문에 모든 서브클래스가 상위 클래스의 알고리즘에 의존하게 됩니다. 따라서 상위 클래스의 변경이 서브클래스에 영향을 미칠 수 있습니다.
  • 후크 메서드를 제공함으로써 알고리즘의 유연성을 높일 수 있지만, 후크 메서드를 너무 많이 제공하면 서브클래스의 복잡성이 증가할 수 있습니다.

템플릿 메서드 패턴은 알고리즘의 핵심 구조를 보호하면서도 확장 가능하게 유지할 수 있는 강력한 디자인 패턴입니다.

Comments