새소식

design-pattern

[디자인패턴] 템플릿 메소드(Template Method) 패턴

  • -

템플릿 메소드(Template Method) 패턴

템플릿 메소드 패턴은 상위 클래스에 알고리즘의 골격의 정의하고 알고리즘의 일부 구현을 하위 클래스에 위임하여 알고리즘을 재사용할 수 있도록 하는 패턴을 말한다.
템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수 있다.

템플릿 메소드 패턴을 사용하면 부모 클래스에서 알고리즘의 골격을 제공하기 때문에 추후 알고리즘에 변경이 일어나더라도 부모 클래스 한 군데만 수정하면 된다는 장점이 있다.

예시

템플릿 메소드 패턴이 어떻게 구현되는지 예시로 확인해보자.

아래 Tea 클래스는 추상 클래스로 make() 메서드에서 알고리즘의 골격을 정의한다.
make() 메서드는 내부에서 여러 메서드를 호출하는데, 이 중 일부는 추상 메서드이기 때문에 Tea를 상속한 하위 클래스에서 구현해야 한다.
Teamake() 메서드는 알고리즘을 이루는 메서드들이 언제 어떤 흐름으로 호출될 지 정의해둔 것으로 프레임워크로 볼 수 있다.

public abstract class Tea {
    private String teaName;

    public Tea(String teaName) {
        this.teaName = teaName;
    }

    public final void make() {
        System.out.println("tea '" + teaName + "' make start");
        prepare();
        boil();
        if (hasAdditional()) {
            addCondiment();
        }
        complete();
        System.out.println("tea '" + teaName + "' make end");
    }

    protected abstract void prepare();

    private void boil() {
        System.out.println("물 끓이기");
    }

    protected boolean hasAdditional() {
        return false;
    }

    protected abstract void addCondiment();

    protected abstract void complete();
}

아래는 Tea 클래스를 상속받은 SimpleTea 클래스이다.
Tea 클래스에서 추상 메서드로 정의한 메서드들을 여기서 구현한다.
SimpleTea는 추상 메서드를 간단하게 구현했다.

public class SimpleTea extends Tea {

    public SimpleTea(String teaName) {
        super(teaName);
    }

    @Override
    protected void prepare() {
        System.out.println("[SimpleTea] prepare");
    }

    @Override
    protected void addCondiment() {}

    @Override
    protected void complete() {
        System.out.println("[SimpleTea] No condiments SimpleTea Complete");
    }
}

아래는 Tea 클래스를 상속받은 CustomTea 클래스이다.
CustomTeaSimpleTea와 다르게 사용자로부터 첨가물을 입력받아 complete() 메서드에서 입력받은 내용을 출력한다.
또한 hasAdditional() 메서드는 추상 메서드는 아니지만, 오버라이딩해서 첨가물을 입력받을 수 있도록 했다.

public class CustomTea extends Tea {
    private String condiments;

    public CustomTea(String teaName) {
        super(teaName);
    }

    @Override
    protected void prepare() {
        System.out.println("[CustomTea] prepare");
    }

    @Override
    protected boolean hasAdditional() {
        return true;
    }

    @Override
    protected void addCondiment() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("[CustomTea] 첨가물을 입력하세요.");
        System.out.println("[CustomTea] 아래 중 한가지를 선택해서 번호를 입력해주세요.");
        System.out.println("[CustomTea] 0. 추가 안함");
        System.out.println("[CustomTea] 1. 포도");
        System.out.println("[CustomTea] 2. 레몬");
        System.out.println("[CustomTea] 3. 자몽");
        try {
            int num = Integer.parseInt(br.readLine());
            switch (num) {
                case 0:
                    break;
                case 1:
                    this.condiments = "포도";
                    break;
                case 2:
                    this.condiments = "레몬";
                    break;
                case 3:
                    this.condiments = "자몽";
                    break;
                default:
                    throw new RuntimeException("입력 오류 발생");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void complete() {
        StringBuffer sb = new StringBuffer();
        sb.append("[CustomTea] ");
        if (this.condiments == null) {
            sb.append("No condiments CustomTea Complete");
        } else {
            sb.append("Condiments name ");
            sb.append("'").append(condiments).append("' CustomTea Complete");
        }
        System.out.println(sb);
    }
}

추상 클래스를 상속받은 SimpleTeaCustomTea를 구현했으므로 이를 생성하여 사용해보자.

public class Main {

    public static void main(String[] args) {
        List<Tea> teas = new ArrayList<>();
        teas.add(new SimpleTea("단순 티"));
        teas.add(new CustomTea("커스텀 티"));
        for (Tea tea : teas) {
            System.out.println("=======================");
            tea.make();
        }
    }
}

다음과 같은 출력을 확인할 수 있다.

=======================
tea '단순 티' make start
[SimpleTea] prepare
물 끓이기
[SimpleTea] No condiments SimpleTea Complete
tea '단순 티' make end
=======================
tea '커스텀 티' make start
[CustomTea] prepare
물 끓이기
[CustomTea] 첨가물을 입력하세요.
[CustomTea] 아래 중 한가지를 선택해서 번호를 입력해주세요.
[CustomTea] 0. 추가 안함
[CustomTea] 1. 포도
[CustomTea] 2. 레몬
[CustomTea] 3. 자몽
2
[CustomTea] Condiments name '레몬' CustomTea Complete
tea '커스텀 티' make end

Process finished with exit code 0

결론

위 예시와 같이 템플릿 메소드 패턴을 직접 구현해보았다.
알고리즘의 전체적인 흐름을 정의한 메서드를 작성하고, 전체 알고리즘을 세부적으로 나눈 추상 메서드를 상위 클래스(Tea)에 선언하여 알고리즘의 흐름을 한 곳에서 관리할 수 있도록 하였다.
확실히 알고리즘의 흐름을 수정할 필요가 있다면 Tea만 수정하면 되기 때문에 편리해보인다.

Tea의 추상 메서드들을 구현한 하위 클래스 SimpleTeaCustomTea에서 각각 서로에게 맞는 알고리즘의 내부 구현을 작성할 수 있었고, 필요에 따라 Tea에 기본으로 정의된 메서드를 오버라이딩하여(TeahasAdditional() 메서드) Tea에 작성된 알고리즘의 흐름을 프레임워크가 지원하는 내에서 변경할 수 있었다.

템플릿 메소드 패턴의 특징과 장점을 잘 활용해보자.

[디자인패턴] 템플릿 메소드(Template Method) 패턴

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.