프로그래밍/알고리즘&자료구조&패턴

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

lazy_web_devloper 2025. 8. 18. 10:55
728x90
반응형

JAVA로 구현하는 알고리즘과 디자인 패턴 공부: 템플릿 메서드 패턴 (Template Method)

1. 템플릿 메서드 패턴이란? 📝

개념: 알고리즘의 뼈대(구조)를 상위 클래스에 정의하고, 알고리즘의 일부 단계를 하위 클래스에서 재정의할 수 있도록 하는 패턴입니다. 즉, 전체적인 흐름은 고정하되, 세부적인 내용은 하위 클래스가 자유롭게 바꿀 수 있게 해줍니다.

요리 레시피를 생각하면 이해하기 쉽습니다. 어떤 요리든 '재료 준비 → 조리 → 그릇에 담기'라는 기본 절차(템플릿)가 있습니다. 여기서 '조리'라는 단계는 메뉴(라면, 카레 등)에 따라 구체적인 방법이 달라집니다. 템플릿 메서드 패턴은 이처럼 전체적인 절차(템플릿 메서드)는 상위 클래스가 정하고, 구체적인 내용(추상 메서드)은 하위 클래스가 채우도록 하는 구조입니다.

2. 왜 사용할까요?

  • 코드 중복 방지: 여러 클래스에서 공통적으로 사용되는 알고리즘 구조를 상위 클래스로 옮겨 중복을 제거할 수 있습니다.
  • 구조의 일관성 유지: 하위 클래스들이 알고리즘의 전체 구조를 변경하는 것을 막고, 정해진 틀 안에서 특정 부분만 수정하도록 강제하여 일관성을 유지할 수 있습니다.
  • 확장성: 새로운 기능을 추가할 때, 상위 클래스의 템플릿을 상속받아 필요한 부분만 구현하면 되므로 확장이 용이합니다.

3. Java로 구현하는 방법

시나리오: 다양한 종류의 파일을 처리하는 데이터 프로세서를 만들어 보겠습니다. 모든 파일 처리는 '파일 열기 → 데이터 처리 → 파일 저장'의 단계를 따르지만, '데이터 처리' 방식은 파일 종류(CSV, JSON)에 따라 다릅니다.

1단계: 추상 클래스(Abstract Class)에 템플릿 정의

알고리즘의 뼈대를 정의하는 상위 클래스입니다.

  • process()라는 템플릿 메서드를 final로 선언하여 하위 클래스가 구조를 변경하지 못하게 합니다.
  • 템플릿 메서드는 일련의 단계를 순서대로 호출합니다.
  • processData()처럼 하위 클래스가 반드시 구현해야 하는 단계는 **추상 메서드(abstract method)**로 선언합니다.
Java
 
public abstract class DataProcessor {

    // 이것이 바로 '템플릿 메서드'입니다.
    // 알고리즘의 전체 흐름을 정의하며, final로 선언하여 재정의를 막습니다.
    public final void process() {
        openFile();
        processData();
        saveData();
    }

    // 공통적으로 사용되는 단계는 일반 메서드로 구현합니다.
    private void openFile() {
        System.out.println("파일을 엽니다.");
    }

    private void saveData() {
        System.out.println("처리된 데이터를 저장합니다.");
    }

    // 하위 클래스마다 다르게 구현될 부분은 추상 메서드로 선언합니다.
    protected abstract void processData();
}

2단계: 구체적인 하위 클래스(Concrete Class) 구현

추상 클래스를 상속받아, 알고리즘의 비어있는 부분을 구체적으로 구현합니다.

Java
 
// CSV 파일을 처리하는 하위 클래스
public class CsvDataProcessor extends DataProcessor {
    @Override
    protected void processData() {
        System.out.println("CSV 포맷에 맞춰 데이터를 처리합니다.");
    }
}

// JSON 파일을 처리하는 하위 클래스
public class JsonDataProcessor extends DataProcessor {
    @Override
    protected void processData() {
        System.out.println("JSON 포맷에 맞춰 데이터를 처리합니다.");
    }
}

3단계: 클라이언트 코드에서 사용하기

클라이언트는 구체적인 클래스를 선택하여 템플릿 메서드만 호출하면, 알아서 정해진 절차에 따라 작업이 수행됩니다.

Java
 
public class Main {
    public static void main(String[] args) {
        // CSV 파일 처리
        DataProcessor csvProcessor = new CsvDataProcessor();
        csvProcessor.process();

        System.out.println("---");

        // JSON 파일 처리
        DataProcessor jsonProcessor = new JsonDataProcessor();
        jsonProcessor.process();
    }
}

4. 템플릿 메서드 패턴과 실제 사례

  • 스프링의 *Template 클래스들: JdbcTemplate, RestTemplate 등 스프링에서 'Template'이라는 이름이 붙은 클래스들은 대부분 이 패턴을 활용합니다. 예를 들어 JdbcTemplate은 데이터베이스 연결, Statement 생성, 예외 처리, 리소스 반납 등 반복적이고 정형화된 작업(템플릿)을 모두 처리해 줍니다. 개발자는 RowMapper나 PreparedStatementSetter처럼 매번 달라지는 부분(SQL 쿼리, 결과 매핑 로직)만 구현하여 전달하면 됩니다.
  • Java 서블릿(Servlet): HttpServlet 클래스의 service() 메서드는 HTTP 요청(GET, POST 등)의 종류를 파악하고 그에 맞는 doGet(), doPost() 등을 호출하는 템플릿 역할을 합니다. 개발자는 doGet()이나 doPost() 메서드만 필요에 맞게 재정의할 뿐, service()의 전체적인 요청 처리 흐름을 바꾸지는 않습니다.

5. 문제 제시 및 답변 💡

문제: 게임 캐릭터가 몬스터를 공격하는 로직을 만드려고 합니다. 모든 공격은 '무기 꺼내기 → 공격 모션 → 데미지 계산'의 3단계로 이루어집니다. 하지만 '공격 모션'과 '데미지 계산' 방식은 캐릭터의 직업(전사, 마법사)에 따라 달라집니다.

템플릿 메서드 패턴을 사용하여 이 공격 시스템을 어떻게 설계할 수 있을까요?

답변:

데이터 프로세서 예제와 매우 유사하게 설계할 수 있습니다.

  1. Character 추상 클래스:
    • final로 선언된 attack() 템플릿 메서드를 만듭니다.
    • attack() 메서드는 takeOutWeapon(), performAttackMotion(), calculateDamage()를 순서대로 호출합니다.
    • takeOutWeapon()은 모든 직업이 동일하므로 일반 메서드로 구현합니다.
    • performAttackMotion()과 calculateDamage()는 직업마다 다르므로 추상 메서드로 선언합니다.
  2. Warrior와 Mage 클래스:
    • Character 추상 클래스를 상속받습니다.
    • Warrior는 칼을 휘두르는 모션과 물리 데미지 계산 로직을 구현합니다.
    • Mage는 마법을 시전하는 모션과 마법 데미지 계산 로직을 구현합니다.

이렇게 설계하면, 어떤 직업이든 character.attack()이라는 동일한 코드로 공격을 실행할 수 있으며, 전체 공격의 흐름을 일관되게 유지하면서 각 직업의 특성을 유연하게 확장할 수 있습니다.

728x90
반응형