본문 바로가기

언어/Java

[ Java ] Template Method Pattern - 템플릿 메서드 패턴 알아보기

반응형

템플릿 메서드 패턴 (Template Method Pattern)

1. 정의
템플릿 메서드 패턴(Template Method Pattern)은 상위 클래스에서 알고리즘의 골격을 정의하고, 하위 클래스에서 그 알고리즘의 특정 단계들을 구체화하는 디자인 패턴입니다. 즉, 알고리즘의 구조는 유지하되, 그 세부적인 동작은 하위 클래스에서 구현하는 방식입니다.

이 패턴을 사용하면 알고리즘의 공통 부분을 상위 클래스에 두고, 세부적인 차이점만 하위 클래스에서 오버라이드하여 구현할 수 있습니다. 이렇게 하면 코드 중복을 줄이고, 공통된 프로세스에서 세부 구현을 유연하게 처리할 수 있습니다.

2. 목적

  • 알고리즘의 공통 구조를 정의: 상위 클래스에서 알고리즘의 전체적인 흐름을 정의하고, 알고리즘의 구체적인 단계는 하위 클래스에서 구현할 수 있게 함으로써 코드의 재사용성을 높입니다.
  • 행동의 변화를 캡슐화: 알고리즘의 일부 단계를 하위 클래스에서 재정의할 수 있으므로, 전체 알고리즘의 변경 없이 행동을 쉽게 변경할 수 있습니다.

3. 사용 예시
템플릿 메서드 패턴은 다음과 같은 경우에 유용합니다:

  • 알고리즘의 구조는 동일하지만, 특정 단계만 다르게 구현해야 할 때
  • 전체 흐름을 고정시키고, 특정 부분만 다양하게 처리하고 싶을 때
  • 중복 코드를 줄이기 위해 공통 로직을 상위 클래스에 정의하고, 차이점만 하위 클래스에서 처리하고 싶을 때

4. 구현 방법
템플릿 메서드 패턴에서 주로 사용하는 구성 요소는 다음과 같습니다:

  1. 템플릿 메서드: 알고리즘의 뼈대를 정의하는 메서드로, 상위 클래스에서 전체 프로세스를 결정합니다. 이 메서드는 재정의되지 않으며, 알고리즘의 전체 흐름을 제공합니다.
  2. 추상 메서드(혹은 구체 메서드): 알고리즘의 각 단계를 하위 클래스에서 구체화하도록 요구하거나, 상위 클래스에서 기본 동작을 제공할 수 있습니다.

다음은 템플릿 메서드 패턴을 사용한 간단한 예시입니다.

// 1. 상위 클래스 (알고리즘의 구조 정의)
abstract class Meal {
    // 템플릿 메서드: 요리 과정을 정의
    public final void prepareMeal() {
        prepareIngredients();
        cook();
        eat();
        cleanUp();
    }

    // 요리 과정의 각 단계 (하위 클래스에서 구현)
    protected abstract void prepareIngredients();
    protected abstract void cook();

    // 공통된 메서드 (하위 클래스에서 재정의하지 않음)
    private void eat() {
        System.out.println("Eating the meal.");
    }

    // 선택적 메서드 (필요 시 하위 클래스에서 재정의 가능)
    protected void cleanUp() {
        System.out.println("Cleaning up after the meal.");
    }
}

// 2. 구체적인 클래스 1 (하위 클래스)
class PastaMeal extends Meal {
    @Override
    protected void prepareIngredients() {
        System.out.println("Preparing pasta and sauce.");
    }

    @Override
    protected void cook() {
        System.out.println("Cooking pasta and heating sauce.");
    }
}

// 3. 구체적인 클래스 2 (하위 클래스)
class SteakMeal extends Meal {
    @Override
    protected void prepareIngredients() {
        System.out.println("Preparing steak and seasoning.");
    }

    @Override
    protected void cook() {
        System.out.println("Grilling the steak.");
    }

    @Override
    protected void cleanUp() {
        System.out.println("Cleaning up the grill.");
    }
}

// 4. 클라이언트 코드
public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        Meal pastaMeal = new PastaMeal();
        pastaMeal.prepareMeal();  // 파스타 요리 과정 출력

        System.out.println();

        Meal steakMeal = new SteakMeal();
        steakMeal.prepareMeal();  // 스테이크 요리 과정 출력
    }
}

5. 동작 설명

  • Meal 클래스는 템플릿 메서드 prepareMeal()을 제공하여 요리 과정을 정의합니다. prepareMeal()은 고정된 알고리즘의 구조로, 요리 재료 준비, 요리, 식사, 정리 과정을 순차적으로 실행합니다.
  • 구체적인 요리 과정은 하위 클래스에서 prepareIngredients()cook() 메서드를 통해 각각 구현합니다.
  • cleanUp() 메서드는 선택적인 메서드로, 하위 클래스에서 필요에 따라 재정의할 수 있습니다. 예를 들어, SteakMealcleanUp() 메서드를 재정의하여 스테이크 조리에 맞는 정리 작업을 수행합니다.

6. 장점

  • 코드 중복 제거: 알고리즘의 공통된 구조는 상위 클래스에서 정의하고, 구체적인 부분은 하위 클래스에서 구현하므로 중복 코드가 줄어듭니다.
  • 유연성 제공: 전체 알고리즘의 구조는 변경하지 않으면서도, 하위 클래스에서 특정 부분의 구현을 변경할 수 있으므로 유연성이 높습니다.
  • 알고리즘 제어: 상위 클래스에서 알고리즘의 전체 흐름을 제어하기 때문에, 알고리즘의 구조가 일관성을 유지하면서도 개별 단계는 하위 클래스에서 구현할 수 있습니다.

7. 단점

  • 구조 복잡성 증가: 알고리즘의 세부 구현이 많아지면 상위 클래스와 하위 클래스 간의 상호작용이 복잡해질 수 있습니다.
  • 상속에 의존: 템플릿 메서드 패턴은 상속을 기반으로 하기 때문에, 유연성이 덜할 수 있으며, 상속 관계가 많아지면 유지보수가 어려워질 수 있습니다.
  • 하위 클래스의 구현 책임 증가: 하위 클래스에서 반드시 구현해야 할 추상 메서드가 많아지면, 하위 클래스에서 부담이 커질 수 있습니다.

8. 템플릿 메서드 패턴이 유용한 경우

  • 알고리즘의 공통된 구조가 있지만, 세부적인 동작이 다른 경우: 예를 들어, 다양한 종류의 게임에서 기본적인 게임 흐름(시작, 플레이, 종료)은 동일하지만, 각 게임의 규칙이나 세부적인 동작은 다를 때 유용합니다.
  • 비슷한 작업을 여러 단계로 나누어 처리해야 할 때: 웹 애플리케이션에서 요청을 처리하는 과정(인증, 로깅, 비즈니스 로직 실행 등)을 템플릿 메서드 패턴으로 구현할 수 있습니다.

9. 실제 사용 사례

  • 프레임워크 기반의 개발: 스프링(Spring) 같은 프레임워크에서 템플릿 메서드 패턴을 자주 볼 수 있습니다. 예를 들어, 트랜잭션 관리나 데이터베이스 접근에서 템플릿 메서드 패턴이 사용됩니다.
  • 파일 처리: 파일 읽기, 쓰기, 폐쇄 등 여러 단계로 구성된 작업에서 템플릿 메서드 패턴을 사용할 수 있습니다.
  • 게임 개발: 다양한 게임에서 동일한 게임 흐름(시작, 실행, 종료)을 사용하되, 각 게임의 구체적인 로직을 하위 클래스에서 구현하는 경우.

템플릿 메서드 패턴은 알고리즘의 공통된 흐름을 유지하면서도, 특정 단계의 구현을 하위 클래스에서 유연하게 처리하고 싶을 때 매우 유용한 패턴입니다. 이 패턴은 특히 코드 중복을 줄이고, 알고리즘의 구조를 일관성 있게 유지하면서 다양한 변화를 적용할 수 있는 상황에서 효과적입니다.

반응형