디자인패턴 - 템플릿 메서드 패턴

JeongHoHyun·2025년 3월 7일

Java

목록 보기
18/23

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

상위 클래스(추상 클래스)에서 알고리즘의 구조를 정의 하고, 하위 클래스에서 세부적인 구현을 담당하도록 하는 디자인 패턴.
즉. 일부 동작은 고정된 템플릿(틀)으로 제공하고, 나머지는 하위 클래스가 구현하도록 한다.
작업에서 알고리즘의 골격을 정의하고, 일부 단계를 하위 클래스로 연기한다.
템플릿 메서드를 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의 할 수 있다.

  • 부모 클래스에 알고리즘의 골격인 템플릿을 정의하고, 일부 변경되는 로직은 자식 클래스에 정의하는 것이다.
  • 이렇게 하면 알고리즘의 전체 구조를 변경하지 않고 특정 부분만 재정의 할 수 있다.

핵심 개념

  • 상위 클래스(추상 클래스) 에서 알고리즘의 흐름을 결정(정의) 하고, 하위 클래스에서 특정 단계(step)의 동작을 오버라이딩하여 구현할 수 있도록 한다.
  • 코드 중복을 줄이고 일관성을 유지하면서 유연성을 제공한다.

구조

  1. 추상 클래스(Abstrat Class)
    • 알고리즘의 뼈대를 정의.
    • 특정부분(훅 메서드, hook method)을 abstract메서드로 선언하여 하위 클래스에서 구현하도록 함.
  2. 구체 클래스(Concrete Class)
    • 상위 클래스에서 정의된 템플릿 메서드를 기반으로 세부 로직을 직접 구현

예제 (Java)

템플릿이 되는 추상 클래스

// 추상 클래스 (템플릿 메서드 포함)
public abstract class CoffeeTemplate {
    
    // 템플릿 메서드 (알고리즘의 흐름을 정의)
    public final void prepareCoffee() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {  // 훅(hook) 메서드
            addCondiments();
        }
    }

    // 공통적인 단계 (구현 제공)
    private void boilWater() {
        System.out.println("물을 끓인다.");
    }

    private void pourInCup() {
        System.out.println("컵에 따른다.");
    }

    // 서브 클래스가 반드시 구현해야 하는 추상 메서드
    protected abstract void brew();
    protected abstract void addCondiments();

    // 서브 클래스에서 선택적으로 오버라이드할 수 있는 훅 메서드
    protected boolean customerWantsCondiments() {
        return true;
    }
}

구체적인 종류를 구현하는 클래스

// 아메리카노 클래스
public class Americano extends CoffeeTemplate {
    @Override
    protected void brew() {
        System.out.println("에스프레소를 내린다.");
    }

    @Override
    protected void addCondiments() {
        System.out.println("추가적인 첨가물을 넣지 않는다.");
    }
}
// 라떼 클래스
public class Latte extends CoffeeTemplate {
    @Override
    protected void brew() {
        System.out.println("에스프레소를 내린다.");
    }

    @Override
    protected void addCondiments() {
        System.out.println("우유를 추가한다.");
    }
}
// 카페모카 클래스 (훅 메서드를 이용한 예제)
public class CafeMocha extends CoffeeTemplate {
    @Override
    protected void brew() {
        System.out.println("에스프레소를 내린다.");
    }

    @Override
    protected void addCondiments() {
        System.out.println("초콜릿 시럽과 우유를 추가한다.");
    }

    @Override
    protected boolean customerWantsCondiments() {
        return false;  // 첨가물을 추가하지 않음
    }
}

실행 코드

public class TemplateMethodExample {
    public static void main(String[] args) {
        System.out.println("===== 아메리카노 만들기 =====");
        CoffeeTemplate americano = new Americano();
        americano.prepareCoffee();

        System.out.println("\n===== 라떼 만들기 =====");
        CoffeeTemplate latte = new Latte();
        latte.prepareCoffee();

        System.out.println("\n===== 카페모카 만들기 =====");
        CoffeeTemplate cafeMocha = new CafeMocha();
        cafeMocha.prepareCoffee();
    }
}

장점과 단점

✅ 장점

  1. 코드 재사용성 증가.
    • 공통된 알고리즘의 뼈대를 유지하면서, 각 세부 구현을 변경할 수 있어 코드 중복이 줄어든다.
  2. 유지보수 용이.
    • 알고리즘의 흐름이 한 곳에서 관리되므로 유지보수가 쉬움.
  3. OCP(개방-폐쇠 원칙)준수.
    • 새로운 행동을 추가할 때 기존 코드를 수정할 필요 없이 새로운 서브 클래스를 만들기만 하면 됨.

❌ 단점

  1. 하위 클래스 증가.
    • 각기 다른 동작을 구현하기 위해 여러 개의 하위 클래스를 생성해야 할 수 있다.
  2. 유연성이 제한될 수 있음.
    • 알고리즘의 구조가 상위 클래스에 고정되어 있으므로, 큰 변경이 필요하면 템플릿 메서드 패턴을 수정해야 한다.
    • 템플릿 메서드 패턴은 상속을 사용하기 때문에 상속에서 오는 단점을 그대로 안고 간다.
    • 특히 자식클래스가 부모 클래스가 컴파일 시점에 강하게 결합되는 문제가 있다.
    • 자식클래스의 입장에서는 부모클래스의 기능을 전혀 사용하지 않는다.
    • 상속 받는다는 것은 특정 부모클래스를 의존하고 있다는 것이다.

어디서 사용될까?

  • 프레임워크 개발
    • Spring의 AbstractApplicationContext 클래스처럼 초기화 로직을 템플릿화 하여 서브 클래스가 확장하도록 만듦.
  • 게임 개발
    • 게임 캐릭터의 공격/방어 패턴을 템플릿화 하고, 각 캐릭터 별로 다른 스킬을 구현.
  • 파일 파싱
    • CSV, JSON, XML 등의 데이터를 읽고 파싱하는 로직을 공통으로 제공하고, 각 파일 형식별로 세부 구현을 다르게 함.
profile
Java Back-End 2022.11.01 💻~ing

0개의 댓글