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

wannabeking·2022년 10월 14일
0

디자인 패턴

목록 보기
10/14
post-thumbnail

취준생의 루틴

월요일날 일어나서 Java 공부하고 피곤하면 자거나 유튜브 본다.
화요일날 일어나서 CS 공부하고 컴퓨터 게임한다.
수요일은...
-어느 취준생의 루틴-

위 취준생은 각 요일마다 비슷한 하루 일과를 보낸다.

기상 -> 무언가 공부 -> 놀기(졸리면 패스)

물론 월요일 루틴, 화요일 루틴 클래스를 추상화 없이 만들 수 있다.
하지만 지금까지 OOP와 디자인 패턴을 공부한 짬밥이 있지 않은가.

이번 시간에는 템플릿 메소드 패턴에 대해 공부하여 예제에 적용해보자.



템플릿 메소드 패턴

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

코드를 살펴보자.


public abstract class Routine {

    final void carryOut() {
        wakeUp();
        study();
        if (!sleepy()) {
            play();
        }
    }

    void wakeUp() {
        System.out.println("기상");
    }

    abstract void study();

    abstract void play();

    boolean sleepy() {
        return false;
    }
}

각 요일의 일과는 기상 -> 공부 -> 놀기로 진행된다.

  • 기상은 무슨 요일이든 동일하다.
  • 공부는 요일마다 다르다.
  • 놀기또한 요일마다 다르며 월요일은 피곤하기 때문에 생략할 수도 있다.

그렇다면 추상 클래스 Routine으로 wakeUp()은 구현하고 study(), play()는 추상 메소드로 남겨 놓으면 될 것 같다.

하루 일과의 순서는 매번 동일하기 때문에 carryOut()을 final으로 구현하여 서브클래스에서 재정의 불가하게 만들었다.

여기서 sleepy()hook이라는 템플릿 메소드의 감초같은 녀석이다.
hook은 필요에 따라서 재정의 해주면 된다.
예제에서 월요일은 피곤하면 잔다고 했으니, 사용자에게 피곤한지 입력 받아 피곤하다면 play()를 생략하도록 구현할 수 있을 것이다.
화요일은 항상 놀기 때문에 재정의를 안해주면 그만이다.

그럼 이제 월요일과 화요일의 일과를 구현해보자.


public class MondayRoutine extends Routine {

    @Override
    void study() {
        System.out.println("월요일은 역시 Java 공부지!");
    }

    @Override
    void play() {
        System.out.println("주말에 실컷 놀았으니 유튜브 조금만 보다 자자.");
    }

    @Override
    boolean sleepy() {
        String answer = getUserInput();
        boolean ret;

        switch (answer) {
            case "y":
                ret = true;
                break;
            case "error":
                System.out.println("error");
                ret = false;
                break;
            default:
                ret = false;
                break;
        }

        return ret;
    }

    private String getUserInput() {
        System.out.print("공부하느라 피곤한가요? (y / 나머지) : ");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            return br.readLine();
        } catch (IOException e) {
            return "error";
        }
    }
}

우선 월요일이다.

월요일엔 Java 공부, 유튜브를 조금만 보며 놀지만 피곤하면 자야되니 hook을 사용자에게 피곤한지 입력 받게 오버라이딩했다.


public class TuesdayRoutine extends Routine {

    @Override
    void study() {
        System.out.println("화요일은 CS 공부 하는 날.");
    }

    @Override
    void play() {
        System.out.println("화요일이니 컴퓨터 게임을 해볼까~");
    }
}

화요일엔 CS 공부하고 컴퓨터 게임을 하는 날이다.

psvm과 출력을 살펴보자.


public class Main {

    public static void main(String[] args) {
        MondayRoutine mondayRoutine = new MondayRoutine();
        TuesdayRoutine tuesdayRoutine = new TuesdayRoutine();

        mondayRoutine.carryOut();
        System.out.println();
        tuesdayRoutine.carryOut();
    }
}


템플릿 메소드 패턴을 사용하면서 서브클래스의 역할이 줄어들었다.
서브클래스는 단순히 짜여있는 템플릿에서 필요한 일부 단계만 구현하거나 재정의했기 때문이다.

따라서 템플릿 메소드 패턴은 중복 코드를 줄이고 핵심 로직의 관리를 줄일 수 있다는 장점이 있지만, 알고리즘의 일부를 슈퍼클래스에서 구현한 메소드에 의존해야한다는 단점이 있다. (상속보다 구성을!)


모든 소스코드는 여기에서 확인할 수 있다.



profile
내일은 개발왕 😎

0개의 댓글