컴퓨터과학/0 + 소프트웨어 아키텍처(디자인 패턴)

[소프트웨어 아키텍처] 5. 템플릿 메소드 패턴(Template method Pattern -java)

힘들면힘을내는쿼카 2022. 10. 25. 12:33
728x90
반응형

템플릿 메소드 패턴


커멘드 패턴에서 우리는 메서드의 호출을 캡슐화 했습니다.
이번에는 서브클래스에서 언제든 필요할 때마다 알고리즘을 가져다가 사용 할 수 있도록 캡슐화 해보겠습니다.
그리고 할리우드에서 영감을 받아 만들어진 디자인 원칙도 같이 배워보도록 합시다.!

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

간단하게 말하면, 알고리즘의 템플릿(틀)을 만듭니다.
템플릿이란 그냥 메서드 입니다.(템플릿 안에는 또 다른 메서드가 존재합니다.)
좀더 구체적으로 이야기 하면 알고리즘의 정의한 메서드 입니다.

즉, 템플릿 메서드 패턴은 알고리즘 캡슐화 하는 것 입니다.

변하지 않는 부분은 부모클래스에서 구현하고, 변하는 부분은 자식클래스가 오버라이딩 합니다.

 

 

템플릿 메소드는
알고리즘의 각 단계를 정의하며,
서브클래스에서 일부 단계를 구현할 수 있도록 유도 합니다.

 

PPT 템플릿으로 예를 들어보죠

PPT 템플릿을 다운로드 받으면 전체적인 틀을 유지하면서, 세부적인 사항들은 발표 주제에 맞게 수정합니다.

 

상위 클래스에서 정의하는 부분(PPT 전체 구성)은 템플릿 메소드라 하고,
하위 클래스마다 다르게 작성되야 하는 일부분(발표 주제에 맞게 수정하는 부분)을 훅(hook)이라고 합니다.
훅(hook)은 하위 클래스가 추상 클래스에서 진행되는 작업을 처리할지 말지 결정하게 하는 기능을 부여하는 용도로 사용 가능하다.
알고리즘의 특정 단계가 선택적으로 적용된다면 훅(hook)을 사용하면 된다.

 

코드의 중복 제거를 위해 흔히 사용하는 패턴입니다.

 

할리우드 원칙

 

먼저 연락하지 마세요. 저희가 연락 드리겠습니다…!

 

할리우드 원칙을 사용하면 의존성 부패(dependency rot)을 방 지 할수 있다.

어떤 고수준 구성 요소가 저수준 구성 요소에 의존하고, 그 저수준 구성 요소는 다시 고수준 구성요소에 의존하고,,,,,,
이런식으로 의존성이 복잡하게 꼬여 있는 상황을 의존성이 부패했다고 부른다.

할리우드 원칙을 사용하면, 저수준 구성 요소가 시스템에 접속할 수는 있지만, 언제 어떻게 사용할지는 고수준 요소에서 결정합니다.

 

즉, 고수준 요소저수준 요소에게 “연락하지마세요. 저희가 연락 드릴께요.” 하는것과 같다.

 

템플릿 메소드 구조

  • AbstractClass
    • 템플릿 메소드를 정의하는 클래스
    • primitive() 또는 hook() 메소드를 정의하는 클래스
  • ConcreteClass1
    • AbstractClass 를 상속 받아 primitiveMethod()를 구현한 클래스
  • ConcreteClass2
    • AbstractClass 를 상속 받아 primitiveMethod(), hook()를 구현한 클래스

 

커피 음료 머신 애플리케이션 개발

 

 

변하지 않는 부분은 부모에서 구현하고,

변하는 부분은 자식이 오버라이딩 한다.

 

CoffeeBeverage

package kr.ac.uos.designpattern.practice.template;

public abstract class CoffeeBeverage {

    //템플릿 메소드
    final void createBeverage() {
        choiceMenu(); // 변하는 부분
        if(!hotOrIce()) {
            pourIce();
        }
        boilWater();
        pourInCup();
    }

    abstract void choiceMenu(); // 변하는 부분

    boolean hotOrIce() {
        return true;
    }

    private void boilWater() {
        System.out.println("물 끓이는 중");
    }

    private void pourInCup() {
        System.out.println("컵에 따르는 중");
    }

    private void pourIce() {
        System.out.println("얼음이 나오는중");
    }
}

Americano

public class Americano extends CoffeeBeverage {
    @Override
    public void choiceMenu() {
        System.out.println("아메리카노를 선택했습니다.");
    }

    //hook 재정의
    @Override
    boolean hotOrIce() {
        if(choiceHotOrIce().equals("y")) return false;
        return true;
    }

    private String choiceHotOrIce() {
        String answer = "n";
        System.out.println("아이스로 해드릴까요? y/n");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = in.readLine();
            answer.toLowerCase(); //소문자로 변경
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if(answer.equals("y")) {
            System.out.println("아이스 음료 선택하셨습니다.");
            return "y";
        }
        System.out.println("뜨거운 음료로 선택하셨습니다.");
        return "n";
    }
}

Latte

public class Latte extends CoffeeBeverage{
    @Override
    void choiceMenu() {
        System.out.println("라떼를 선택했습니다.");
    }
}

Client

public class Client {
    public static void main(String[] args) {
        CoffeeBeverage coffeeBeverage1 = new Americano();
        CoffeeBeverage coffeeBeverage2 = new Latte();

        coffeeBeverage1.createBeverage();

        System.out.println("//====================//");

        coffeeBeverage2.createBeverage();
    }
}

결과

아메리카노를 선택했습니다.
아이스로 해드릴까요? y/n
y
아이스 음료 선택하셨습니다.
얼음이 나오는중
물 끓이는 중
컵에 따르는 중
//====================//
라떼를 선택했습니다.
물 끓이는 중
컵에 따르는 중

 

정리

  • 알고리즘 골격을 정의하는 템플릿 메소드가 있다.
  • 알고리즘의 일부 단계를 서브클래스에서 구현할 수도 있다.
  • 알고리즘의 특정 단계를 서브클래서에서 재정의할 수도 있다.
  • 전략 패턴과 템플릿 메소드 패턴은 모두 알고리즘을 캡슐화하는 패턴이지만, 전략 패턴은 서브클래스의 알고리즘을 변경할수 있다.
  • 팩토리 패턴은 특화된 템플릿 메소드 패턴이다.

 

 

 

728x90
반응형