2 분 소요


템플릿 메소드 패턴

정의

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

-GoF design pattern

템플릿 메소드 패턴은 알고리즘의 뼈대를 정의하고 서브 클래스마다 세부 동작 사항을 다르게 구현하는 패턴을 말한다. 변하지 않는 템플릿은 상위 클래스에 만들어두고 자주 변경되거나 확장되는 기능은 하위 클래스에서 만들도록 하여, 템플릿 메소드의 동작 순서는 고정하고 세부 실행 내용을 다양화할 때 사용한다.

구조

img1

Abstract Class

템플릿 메소드를 구현하고, 템플릿 메소드에 사용되는 추상 메소드를 선언한다. 이 추상 메소드는 하위 클래스인 Concreate Class에서 구현된다.

Concreate Class

Abstract Class를 상속하고 추상 메소드를 구현한다. Concreate Class에서 구현한 메소드는 Abstract Class의 템플릿 메소드에서 호출된다.

특징

장점

  1. 공통되는 부분을 묶어서 관리하기 때문에 코드의 중복을 줄일 수 있고 관리하기도 편하다.
  2. 알고리즘의 특정 부분만 재정의하여 변경 사항에 대한 영향을 줄일 수 있다.

단점

  1. 템플릿 메소드에 정의한 골격은 고정돼있기 때문에 유연성이 제한될 수 있다.
  2. 알고리즘이 복잡하다면 템플릿 메소드를 만들거나 유지하기 어려울 수 있다.
  3. 추상 메소드가 많아지면 새로운 서브 클래스를 추가하거나 관리하기가 어려울 수 있다.
  4. 템플릿 메소드의 실행 흐름을 이해해야지만 서브 클래스에서 추상 메소드를 재정의할 수 있다.
  5. 만약 템플릿 메소드의 알고리즘이 변경된다면 서브 클래스에서 재정의한 추상 메소드를 수정해야 할 수도 있다.

지금까지 템플릿 메소드 패턴의 구조와 특징에 대해서 알아봤다. 이제 실제로 어떤식으로 활용될 수 있는지 코드를 통해서 확인하자!

예제 코드

Beverage 클래스 (Abstract Class)

img2

여기서 prepareRecipe()메소드는 템플릿 메소드이다. boilWater(), pour()는 모든 음료가 공통으로 처리해야 하는 로직이라고 생각하고 addCondiments()는 각 음료마다 재료가 다르기 때문에 달라져야 하는 부분이라고 생각해보자. 그러면 공통되는 부분은 나두고 각 음료마다 바뀌는 addCondiments()를 추상 메소드로 선언하여 서브 클래스에서 구현하게 하면 변경을 최소화하면서 각 서브 클래스로 구현을 미루면서 유연한 확장을 할 수 있게 된다.

Americano, ChocolateLatee 클래스 (Concreate Class)

img3

Americano와 ChocolateLatte는 Beverage를 상속받아서 각자 addCondiments()메소드를 구현하게 된다. 그리고 템플릿 메소드인 prepareRecipe()를 호출하게 되면 전체적인 알고리즘 흐름은 동일하지만 세부 실행 내용을 변경할 수 있게 된다.

hook 메소드

hook 메소드는 서브 클래스에서 좀 더 유연하게 템플릿 메소드의 알고리즘 로직을 다양화할 수 있게 도와주는 역할을 수행한다. 일반적으로 추상 메소드가 아닌 일반 메소드로 구현을 하게 된다. 그냥 나두게 되면 원래 설정한 기본 흐름대로 실행되고 서브 클래스에서 오버라이딩하게 되면 상황에 맞게 제어할 수 있다.

간단한 예시 코드를 보면서 이해해보자.

img4

템플릿 메소드인 prepareRecipe() 안에 있는 isCustomerWantsTopping()가 앞에서 말한 hook 메서드이다. 기본적인 요구사항이 토핑을 추가하여 음료를 제공한다고 생각하고 로직을 작성한 것이다. 그런데 어떤 음료에는 토핑이 들어가지 않아야 할 수 있다. 이런 경우에는 서브 클래스에서 hook 메서드를 선택적으로 오버라이딩하여 템플릿 메소드의 흐름을 제어할 수 있다.

img5

Americano에 기본적으로 토핑이 들어가면 안된다고 할 때, hook 메서드인 isCustomerWantsTopping()을 제정의하여 템플릿 메소드 안에있는 로직의 실행 흐름을 제어할 수 있다.

참고