Tech & Programming/Pattern & Design

템플릿 메소드 패턴(template method pattern) 이란?

소스코드 요리사 2019. 1. 18. 00:32

이번에는 템플릿 메소드 패턴(template method pattern)에 대해서 알아보도록 하겠습니다.

이전글들과 마찬가지로 Head First Design Pattern 책을 참고했습니다.


템플릿 메소드 패턴에서는 메소드에서 알고리즘의 골격을 정의합니다.

알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다. 

템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정단계를 재정의 할 수 있습니다.


즉, 템플릿 메소트 패턴은 알고리즘 틀을 만들기 위한 것입니다.

아래 예제 소스코드는 커피, 차를 만드는 과정을 프로그램 한 것입니다.


커피, 차를 만드는 일련의 과정을 메소드로 정의합니다.

그리고,  이 메소드 중 하나 이상이 추상메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현됩니다. 

이렇게 하면 서브클래스에서 일부분을 구현할 수 있도록 하면서도 알고리즘의 구조는 바꾸지 않아도 됩니다.



public abstract class CaffeinBeverageWithHook {
    final void prepareRecipe() {
        // 기본적인 커피 or 차 만드는 프로세스
        boilWater(); // 물을 끓이기
        brew(); //  우려내기
        pourInCup(); // 컵에 따르고
        if(customerWantsCondiments() == true) {
            addCodiments(); // 첨가할 것들 첨가하기
        }
    }
    abstract public void brew();
    abstract public void addCodiments();

    public void boilWater() {
        System.out.println("물을 끓이는 중");
    }
    public void pourInCup() {
        System.out.println("컵에 따르는 중");
    }

    public boolean customerWantsCondiments() {
        return true;
    }

}

public class Coffee extends CaffeinBeverageWithHook {

    @Override
    public void brew() {
        System.out.println("필터를 통해 커피를 우려내는 중");
    }

    @Override
    public void addCodiments() {
        System.out.println("설탕과 우유를 추가하는 중");
    }
	
}

public class Tea extends CaffeinBeverageWithHook {

    @Override
    public void brew() {
        System.out.println("차를 우려내는 중");
    }

    @Override
    public void addCodiments() {
        System.out.println("레몬을 추가하는 중");
    }
	
}
아래는 기본적인 템플릿 메소드 패턴의 클래스 다이어그램 입니다.
(※ 이 클래스 다이어그램은 구루비의 위치에서 발췌했습니다. (http://wiki.gurubee.net/pages/viewpage.action?pageId=1507409))



후크(hook) 

후크는 추상 클래스에서 선언되는 메소드 긴 하지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드 입니다.

이렇게 하면 서브클래스에서는 다양한 위치에서 알고리즘에 끼어들 수도 있고, 그냥 무시하고 넘어 갈 수도 있습니다.



  final void prepareRecipe() {
        // 기본적인 커피 or 차 만드는 프로세스
        boilWater(); // 물을 끓이기
        brew(); //  우려내기
        pourInCup(); // 컵에 따르고
    
		if(customerWantsCondiments() == true) {
            addCodiments(); // 첨가할 것들 첨가하기
        }
    }
	
    public boolean customerWantsCondiments() {
        return true;
    }


바로 위 customerWantsCondiments 메소드가 서브클래스에서 필요에 따라 오버라이드 할 수 있는 메소드 이므로 후크입니다.


디자인 원칙 

할리우드 원칙(Hollywood Principle) : 먼저 연락하지 마세요. 저희가 연락드리겠습니다.


할리우드 원칙을 활용하면 의존성 부패(dependency rot)를 방지 할 수 있습니다. 어떤 고수준 구성요소가 저수준 구성요소에 의존하고, 그 저수준의 구성요소는 다시 고수준의 구성요소에 의존하는 것과 같은 식으로 의존성이 복잡하게 꼬여 있는 것을 의존성 부패라고 부릅니다.

이렇게 의존성이 부패되면 시스템이 어떤식으로 디자인 된 것인지 알아 볼 수 없습니다.

할리 우드 원칙을 사용하면 저수준 구성요소에서 시스템에 접속 할 수는 있지만 언제 어떤식으로 그 구성요소들을 사용할 지는 고수준의 구성요소에서 결정하게 됩니다.

즉, 고수준의 구성요소에서 "먼저 연락하지 마세요. 저희가 연락드리겠습니다" 라고 이야기 하는 것과 같습니다.


위 커피, 티 예제에서 CaffeinBeverageWithHook 클래스는 고수준의 구성요소입니다. 음료를 만드는 방법에 해당하는 알고리즘을 장악하고 메소드 구현이 필요한 상황에서만 서브클래스를 호출합니다.

서브클래스는 메소드의 구현만 담당하지 호출 당하기 전까지 추상클래스를 직접 호출하지 않습니다.

핵심정리

  • 템플릿 메소드에서는 알고리즘의 단계들을 정의하는데 일부 단계는 서브클래스에서 구현하도록 할 수 있습니다.
  • 템플릿 메소드 패던은 코드 재사용에 크게 도움이 됩니다.
  • 템플릿 메소드가 들어 있는 추상클래스에서는 구상메소드, 추상메소드, 후크를 정의할 수 있습니다.
  • 추상메소드는 서브클래스에서 구현합니다.
  • 후크(hook)는 추상 클래스에 들어 있는 아무일도 하지 않거나 기본 행동을 정의하는 메소드로 서브클래스에서 오버라이드할  수 있습니다.
  • 서브클래스에서 템플릿 메소드에 들어 있는 알고리즘을 함수보로 바꾸지 못하게 하고 싶다면 final 로 선언하면 됩니다.
  • 헐리우드 원칙에 의하면 저수준 모듈은 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋습니다.
  • 템플릿 메소드 패턴은 실전에서도 꽤 자주 쓰이지만 반드시 교과서적인 방식으로 적용되지는 않습니다.
  • 스트래티지 패턴과 템플릿 메소드 패던은 모두 알고리즘을 캡슐화 하는 패턴이지만 전자에서는 상속을 후자에서는 구성을 이용합니다.
  • 팩토리 메소드 패턴은 특화된 템플릿 메소드 패턴입니다.


전체 예제 소스 : https://github.com/JunpilPark/DesignPatternStudy/tree/master/TampleteMethod/src/main/java/com/example/template

참고 동영상 :