home > back-end > design-pattern > [design-pattern] template method 패턴

[design-pattern] template method 패턴
back-end design-pattern template method

intro : template method 패턴에 대해서 알아보자

Template Method 패턴이란 ?

Template Method 패턴은 객체지향 설계에서 자주 사용하는 행동(Behavioral) 디자인 패턴 중 하나로, 상위 클래스에서 알고리즘의 구조를 정의하고, 하위 클래스에서 일부 세부적인 동작을 구체적으로 구현하도록 허용하는 패턴이다. 즉, 알고리즘의 골격을 정의하고 세부적인 내용은 서브클래스에서 완성되도록 유도한다.

Template Method 패턴은 언제 사용하는가 ?

Template Method 패턴은 알고리즘의 전체 구조(골격)는 고정하되, 특정 단계만 커스터마이징해야 할 때 사용됩니다. 이 패턴은 공통된 프로세스를 상위 클래스에 정의하고, 세부적인 동작을 하위 클래스에서 구현하도록 한다.

Template Method 패턴 적용 코드 (1)

골격을 정의할 Beverage 추상 클래스, 차후 Beverage를 구현할 하위 클래스가 세부 역할을 정의하게 된다.

abstract class Beverage {
    // Template method (큰 틀과 골격, 이부분은 변하지 않는다.)
    // 하위 클래스에서 재정의가 되지 않도록 final 키워드를 사용하였다.
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    void boilWater() {
        System.out.println("Boiling water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }
    
    // 하위 클래스에서 정의할 메소드들 
    abstract void brew();
    abstract void addCondiments();
}

Beverage 추상 클래스를 상속받아 구현한 Tea, Coffee 클래스이다. 해당 클래스들은 brew, addCondiments 메서드들을 각각 구현한다.

// 실제 구현체 클래스에서 추상 메서드 재정의
class Tea extends Beverage {
    void brew() {
        System.out.println("Steeping the tea");
    }

    void addCondiments() {
        System.out.println("Adding lemon");
    }
}
// 실제 구현체 클래스에서 추상 메서드 재정의
class Coffee extends Beverage {
    void brew() {
        System.out.println("Dripping coffee through filter");
    }

    void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

클라이언트는 다음과 같이 Beverage 타입으로 Tea, Coffee 객체를 생성한다. (다형성의 특징) Beverage 클래스에 미리 정의된 prepareRecipe 메서드는 내부적으로 고유한 골격과 순서를 가지고 잇기 때문에 어떠한 구현체 클래스가 prepareRecipe 메서드를 호출하더라도 메서드 내부의 순서는 다르지 않고, brew, addCondiments 메서드의 Overriding 내용만 다르게 작동한다.

// Client code
public class Main {
    public static void main(String[] args) {
        Beverage tea = new Tea();
        Beverage coffee = new Coffee();

        System.out.println("\nMaking tea...");
        tea.prepareRecipe();

        System.out.println("\nMaking coffee...");
        coffee.prepareRecipe();
    }
}

Template Method 패턴 적용 코드 (2)

위 내용과 비슷한 상황이지만, Template Method가 분기에 따라 다르게 동작하는 상황에 대해서도 적용할 수 있다. (보다 실무에 가까운 예제)

// 해당 패턴이 인터페이스를 사용하지 않는 이유는
// 템플릿 메서드는 상위 클래스에서 구현이 되어 있어야 하기 때문.
// 추상클래스의 사용 이유가 된다.
abstract class DataProcessor {
    // Template method (큰 틀과 골격, 이부분은 변하지 않는다.)
    // 하위 클래스에서 재정의가 되지 않도록 final 키워드를 사용하였다.
    public final void process(String data) {
        loadData(data);
        if (isValidData(data)) {
            processData(data);
            saveData(data);
        } else {
            System.out.println("Data is invalid, processing aborted.");
        }
    }

    // 추상 메서드를 정의하고 하위 클래스에서 구현할수 있도록 정의
    // protected 키워드는 자식 클래스에서는 접근가능한 접근 제어자
    protected abstract void loadData(String data);
    protected abstract boolean isValidData(String data);
    protected abstract void processData(String data);
    protected abstract void saveData(String data);
}

BeveraDataProcessorge 추상 클래스를 상속받아 구현한 CSVDataProcessor, JSONDataProcessor 클래스이다. 해당 클래스들은 loadData, isValidData, processData, saveData 메서드들을 각각 구현한다. 위 예제와 같은 역할이다 결국 상위 클래스에서 정의한 추상 메서드들을 각 클래스의 역할에 맞게 재정의 할 뿐이다.

// 실제 구현체 클래스에서 추상 메서드 재정의
class CSVDataProcessor extends DataProcessor {
    @Override
    protected void loadData(String data) {
        System.out.println("Loading data from CSV file: " + data);
    }

    @Override
    protected boolean isValidData(String data) {
        return data != null && data.contains("CSV");
    }

    @Override
    protected void processData(String data) {
        System.out.println("Processing CSV data");
    }

    @Override
    protected void saveData(String data) {
        System.out.println("Saving CSV data to database");
    }
}
// 실제 구현체 클래스에서 추상 메서드 재정의
class JSONDataProcessor extends DataProcessor {
    @Override
    protected void loadData(String data) {
        System.out.println("Loading data from JSON file: " + data);
    }

    @Override
    protected boolean isValidData(String data) {
        return data != null;
    }

    @Override
    protected void processData(String data) {
        System.out.println("Processing JSON data");
    }

    @Override
    protected void saveData(String data) {
        System.out.println("Saving JSON data to database");
    }
}

클라이언트 코드 즉 Main에서 DataProcessor 타입으로 CSVDataProcessor, JSONDataProcessor 객체들을 사용한다. 이때 실행하는 메서드 process 는 추상클래스인 DataProcessor의 템플릿 메서드 이므로, 상위클래스에서 정의된 순서대로 실행된다. (템플릿 메서드 안에 내부적으로 메서드가 순차적으로 정의 되어 있음)

// Client code
public class Main {
    public static void main(String[] args) {
        DataProcessor csvProcessor = new CSVDataProcessor();
        csvProcessor.process("CSV data");

        System.out.println();

        DataProcessor jsonProcessor = new JSONDataProcessor();
        jsonProcessor.process("XML data");
    }
}

Template Method 패턴 요약

Template Method 패턴은 알고리즘의 골격(공통된 흐름)을 상위 클래스에서 정의하고, 세부적인 구현은 하위 클래스에서 담당하게 하는 행동(Behavioral) 디자인 패턴이다.