[디자인 패턴] 8. 템플릿 메서드 패턴(Template Method Pattern)
이 글은 헤드퍼스트 디자인패턴과 GoF 디자인패턴을 읽고 정리한 글입니다.
1. 템플릿 메서드 패턴(Template Method Pattern)
템플릿 메서드 패턴은 알고리즘의 골격(skeleton)을 정의합니다. 템플릿 메서드를 이용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있습니다.
1.1. 구성 요소
AbstractClass
- 추상 메서드(primitiveOperation)를 정의합니다. 이는 서브클래스가 구현하게 됩니다.
- 템플릿 메서드의 알고리즘의 골격을 정의합니다. 내부적으로 템플릿 메서드는 추상 메서드를 호출합니다.
ConcreteClass
- 추상 메서드(primitiveOperation)을 구현합니다.
ConcreteClass는 AbstractClass 에 의존하여 다양한 알고리즘을 구현합니다.
1.2. 적용 방법
- 공통된 부분이 구현된 다른 클래스들을 찾습니다. 해당 로직들이 공통되는 부분과 바뀔수 있는 부분으로 나뉠 수 있는지 확인합니다.
- 추상 클래스(AbstractClass)를 생성합니다. 공통 부분을 추상 클래스에 이동시켜 템플릿 메서드(Template Method)로 생성합니다. 공통 알고리즘을 수정할 수 없게 하려면 final 키워드를 추가하여 오버라이드할 수 없도록 합니다.
- 달라지는 부분의 의미를 파악하여 적절한 이름의 추상 메서드로 정의합니다. 특정 상황에서만 동작하는 확장된 기능이 필요한 경우 Hook 으로 선언합니다.
- 구현 클래스(ConcreteClass)를 생성하고 Abstract Class에서 선언한 추상 메서드를 알맞는 알고리즘으로 구현합니다.
1.3. 적용 시기
- 바뀌지 않는 부분에 대한. 알고리즘을 한번만 작성하고 싶으며, 바뀌는 부분은 하위 클래스에서 구현하도록 하고 싶을 때 템플릿 메서드 패턴을 사용할 수 있습니다.
- 하위 클래스들의 공통된 부분을 새로운 공통 클래스로 분리하여 코드 중복을 제거하고 싶을 때 템플릿 메서드 패턴을 사용할 수 있습니다.
- 훅을 통해 하위 클래스에서 메인 알고리즘의 특정 지점에 기능을 확장하고 싶을 때 템플릿 메서드 패턴을 사용할 수 있습니다.
1.4. 정리
템플릿 메서드 패턴은 코드 재사용을 위한 기본적인 테크닉으로 알려져 있습니다. 특히 라이브러리의 공통 로직을 구현하는데 많이 사용됩니다.
템플릿 메서드 패턴은 “할리우드 원칙(Hollywood principle)”을 따릅니다. 할리우드 원칙이란 “Don’t call us, we’ll call you” 라고도 하는데요, 상위 클래스만이 하위 클래스를 호출할 수 있으며, 반대로 하위 클래스는 상위 클래스를 호출해서는 안되는 원칙입니다. 템플릿 메서드 패턴에서 하위 클래스는 PrimitiveOperation 을 구현했지만, 이는 AbstractClass 의 TemplateMethod 함수에 의해서만 호출되어야 합니다.
Hook은 하위 클래스에서 기능을 추가로 제공하고 싶을 때 추가할 수 있는 기능입니다. 이는 추상 클래스에 아무 동작도 하지 않는 기본 메서드로 선언하여 구현할 수 있습니다. 하위 클래스는 필요에 따라 이 Hook 메서드를 구현하여 기능을 확장할 수 있습니다.
2. 템플릿 메서드 패턴 구현 시 고려사항
2.1. Using C++ access control - 접근 제한자를 이용하라
저는 Java 개발자이므로 Java의 접근 제한자를 이용하는 방법으로 정리하였습니다.
추상 클래스가 제공하는 primitive operation 은 반드시 하위 클래스에서 구현되어야 합니다. 이를 위해 primitive operation 은 추상 메서드로 선언하여야 합니다. Hook 은 추상 클래스에서도 구현할 수 있으며(아무 동작도 하지 않도록 구현), 하위 클래스가 오버라이드 할 수 있도록 protected 접근 제한자를 붙여줄 수 있습니다. 마지막으로 알고리즘의 골격의 역할을 하는 template method 는 하위 클래스에서 임의로 오버라이드하여 동작을 수정할 수 없도록 final 키워드를 붙여줄 수 있습니다.
- Template Method: final (상속 불가)
- Primitive Operation: abstract (필수로 구현)
- Hook Method: protected (오버라이드 가능)
2.2. Minimizing primitive operations - 추상 메서드를 최소한으로 제공하라
템플릿 메서드 패턴을 설계할 때 가장 중요한 목표는 구현해야할 추상 메서드(Primitive operation)를 최소로 하는 것입니다. 구현해야할 필요가 있는 추상 메서드가 많아질 수록 추가되는 알고리즘의 다양성이 많아지게되며, 클라이언트는 이를 구현하는데 그 만큼 비용이 들게 됩니다.
2.3. Naming Conventions - 메서드 네이밍 컨벤션
템플릿 메서드 패턴에서 상위 클래스인 AbstractClass 는 세 가지 종류의 메서드를 정의하고 있습니다. 이를 구현하는 클라이언트의 입장에서는 직접 코드를 확인하지 않으면 각각의 메서드들이 어떠한 목적을 위해 정의되었는지 파악하기 어려울 수 있습니다. 이를 위해 적절한 메서드 네이밍 컨벤션을 정하고 이를 따르게하여 쉽게 패턴을 이해하고 적용할 수 있습니다.