템플릿 메소드 패턴(template method pattern) 이란?
이번에는 템플릿 메소드 패턴(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("레몬을 추가하는 중");
}
}
후크(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
참고 동영상 :