탬플랫 메소드 패턴(Template Method Pattern)은 알고리즘의 골격을 정의한다.
템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있다.
템플릿 메소드 패턴은 알고리즘의 템플릿을 만든다.
템플릿이란?
일련의 단계로 알고리즘을 정의한 메소드이다.
여러 단계 가운데 하나 이상의 단계가 추상 메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현된다.
- 이러한 방식을 통해 서브클래스가 일부분의 구현을 처리하면서도 알고리즘의 구조는 바뀌지 않는다.
구성 요소
classDiagram class AbstractClass { << abstract >> templeteMethod() primitiveOpertion1()* primitiveOpertion2()* } class ConcreteClass { primitiveOperation1() primitiveOperation2() } class templateMethod { primtiveOperation1() primitiveOpertion2() } AbstractClass <|-- ConcreteClass AbstractClass .. templateMethod
AbstractClass
: 알고리즘의 구조를 정의하며, 일부 단계는 추상 메소드로 선언하여 하위 클래스에게 위임한다.ConcreteClass
: 추상 클래스에서 정의된 추상 메소드르 구현하여 알고리즘의 일부를 구체화한다.
|
|
템플릿 메소드는 알고리즘의 각 단계를 정의하며, 서브클래스에서 일부 단계를 구현할 수 있도록 유도하는데, 위 예시에서 카페인 음료를 만드는 알고리즘 템플릿 메소드는 prepareRecipe()
이다.
- 탬플릿 내에서 알고리즘의 각 단계가 메소드로 표현된다.
- 어떤 메소드는
CaffeineBerverage
에서 처리된다. - 어떠한 메소드는 서브클래스에서 처리된다.
- 서브클래스에서 구현해야 하는 메소드는
abstract
로 선언해야한다.
템플릿 메소드 후크
후크(hook)는 추상 클래스에서 선언되지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드이다.
이러한 방식으로 서브클래스는 다양한 위치에서 알고리즘에 끼어들 수 있다.(무시하고 넘어갈 수도 있다.)
|
|
후크 활용하기
후크를 사용하려면 서브클래스에서 후크를 오버라이드해야 한다.
|
|
- 템플릿을 만들 때 추상 메소드를 써야할 때와 후크를 써야할 때를 어떻게 구분할 수 있는가?
- 서브클래스가 알고리즘이 특정 단계를 제공해야 한다면 추상 메소드를 써야한다.
- 알고리즘의 특정 단계가 선택적으로 적용된다면 후크를 쓴다.
- 후크를 쓰면 서브클래스에서 필요할 때 후크를 구현할 수도 있지만, 꼭 구현하지 않아도 된다.
- 후크의 용도
- 알고리즘에서 필수적이지 않은 부분을 서브클래스에서 구현하도록 만들고 싶을 때
- 템플릿 메소드에서 앞으로 일어날 일이나 막 일어난 일에 서브클래스가 반응할 수 있도록 기회를 제공하는 용도로
- 내부적으로 특정 목록을 재정렬한 후에 서브 클래스에서 특정 작업을 수행하도록 하고 싶은 때 등
- 서브클래스가 추상 클래스에서 진행되는 작업을 처리할 지 말지 결정하게 하는 기능을 부여하는 용도 등
- 모든 서브클래스에서 모든 추상 메소드를 정의해야한다.
- 템플릿 메소드에 있는 알고리즘의 단계 중에서 정의되지 않은 부분을 모두 채워줘야함
- 추상 메소드가 너무 많아지면 서브클래스에서 일일이 추상 메소드를 구현해야 하므로 좋지 않을 수 있다.
- 알고리즘의 단계를 너무 잘게 쪼개지 않는 것도 한 가지 방법이 도리 수 있다.
- 하지만 알고리즘을 큼직한 몇 가지 단계로만 나눠 놓으면 유연성이 떨어질 수 있다.
- 모든 단계가 필수는 아니므로 필수가 아닌 부분을 후크로 구현하면 추상 클래스의 서브클래스를 만들 때 부담이 줄 수 있다.
할리우드 원칙
할리우드 원칙(Hollywood Principle)
먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
할리우드 원칙을 활용하면 의존성 부패(Dependency rot)를 방지할 수 있다.
의존성 부패?
어떤 고수준 구성 요소가 저수준 구성 요소에 의존하고, 그 저수준 구성 요소는 다시 고수준 구성 요소에 의존하게 되어 의존성의 복잡하게 꼬여있는 상황의존성이 부패하면 시스템 디자인이 어떤 식으로 되어있는지 알아보기 힘드므로, 저수준 구성 요소가 시스템에 접속할 수는 있지만 언제, 어떻게 구성 요소를 사용할지는 고수준 구성 요소가 결정해야한다.
할리우드 원칙과 템플릿 메소드 패턴
템플릿 메소드 패턴을 써서 디자인하면 고수준 요소에서 저수준 요소를 필요할 때만 호출하는 형식으로 구현되기 때문에 할리우드 원칙을 적용하게 된다.
할리우드 원칙과 의존성 뒤집기 원칙
의존성 뒤집기 원칙은 될 수 있으면 구상 클래스 사용을 줄이고 추상화된 것을 사용해야 한다는 원칙이라면, 할리우드 원칙은 저수준 구성 요소가 컴퓨테이션에 참여하면서도 저수준 구성 요소와 고수준 계층 간 의존을 없애도록 프레임워크나 구성 요소를 구축하는 기법이다.
따라서 객체를 분리한다는 하나의 목표를 공유하지만, 의존성을 피하는 방법에 있어서 의존성 뒤집기 윈칙이 훨씬 더 강하고 일반적인 내용을 담고 있다.
할리우드 원칙은 저수준 구성 요소를 다양하게 사용할 수 있으면서도 다른 클래스가 구성 요소에 너무 의존하지 않게 만들어 주는 디자인 구현 기법을 제공한다.
템플릿 메소드 패턴과 전략 패턴의 차이점
템플릿 메소드 패턴과 전략 패턴 모두 알고리즘을 대상으로 하고있지만 다른 접근 방식을 취하고있다.
템플릿 메소드 패턴은 알고리즘의 구조를 정의하고 일부 단계를 하위 클래스에 위임하여 확장성을 제공하며, 전략 패턴은 알고리즘을 캡슐화하고 동적으로 변경할 수 있도록 하는 데 중점을 둔다.
목적
- 템플릿 메소드 패턴
- 주로 알고리즘의 구조를 정의하고 일부 단계를 하위 클래스에게 위임하여 확정성을 제공한다.
- 상위 클래스에서 알고리즘의 틀(템플릿)을 제공하고 하위 클래스에서 구체적인 단계를 구현한다.
- 전략 패턴
- 알고리즘을 캡슐화하고 해당 알고리즘을 동적으로 변경할 수 있도록 하는 데 사용된다.
- 알고리즘을 각각의 전략으로 정의하고 이를 동적으로 교체하여 사용한다.
구현 방식
- 템플릿 메소드 패턴
- 상위 클래스에는 알고리즘의 템플릿이 존재하며, 일부 단계는 추상 메소드로 선언되어 하위 클래스에서 구현된다.
- 하위 클래스에서는 알고리즘의 일부를 결정할 수 있다.
- 전략 패턴
- 알고리즘은 전략 인터페이스를 통해 정의도고, 각 전략은 해당 인터페이스를 구현한다.
- 인터페이스를 사용하므로 실행 시에 원하는 전략으로 교체할 수 있다.
확장성
- 템플릿 메소드 패턴
- 알고리즘의 구조를 확장하는 데 적합하며, 새로운 알고리즘을 추가하거나 기존 알고리즘을 변경할 때 유용하다.
- 전략 패턴
- 새로운 전략을 추가하거나 기존 전략을 변경할 때 확장성이 뛰어나다.
- 런타인에 동적으로 전략을 변경할 수 있다.
의존성
- 템플릿 메소드 패턴
- 상위 클래스와 하위 클래스 간에 강한 의존성이 있다.
- 하위 클래스에서는 상위 클래스의 구조를 따라야한다.
- 전략 패턴
- 컨텍스트와 전략 간에는 느슨한 결합이 있으며, 각 전략은 독립적으로 교체될 수 있다.
Java API 속 템플릿 메소드 패턴
템플릿 메소드 패턴은 정말 많이 쓰이는 패턴이므로 쉽게 발견할 수 있다.
하지만 교과서적인 구현과는 다른 템플릿 메소드도 많이 있어 주의 깊게 살펴보지 않으면 템플릿 메소드 패턴이 적용되어 있다는 사실도 모르고 넘어가기 쉽다.
Arrays
정렬
자바의 Arrays
클래스에는 정렬할 때 쓸 수 있는 편리한 템플릿 메소드가 포함되어 있다.
Arrays
에 있는 정렬용 템플릿 메소드에서 알고리즘을 제공하지만, 특정 요소 비교 방법은 compareTo()
메소드로 구현해야한다.
compareTo()
메소드를 구현하기만 하면 템플릿 메소드 패턴으로 구현 된 정렬 알고리즘을 이용하여 배열의 항목들을 정렬해준다.
JFrame
JFrame
은 가장 기본적인 스윙 컨테이너로 paint()
메소드를 상속받는 컨테이너이다.
paint()
메소드는 후크 메소드라서 아무 일도 하지 않지만, 오버라이드하여 특정 화면 영역에 특정 내용을 표시하는 JFrame
의 알고리즘에 사용자가 원하는 그래픽을 추가할 수 있다.
AbstractList
ArrayList
, LinkedList
같은 자바의 리스트 컬렉션은 리스트에서 필요한 기능을 구현해 주는 AbstractList
클래스를 확장한다.
AbstarctList
에는 get()
, size()
추상 메소드에 의존하는 subList()
템플릿 메소드가 있어, AbstarctList
를 확장해서 나만의 리스트를 만들 때는 이 메소드를 구현해야 한다.
정리
- 템플릿 메소드 패턴
- 알고리즘의 골격을 정의한다.
- 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있다.
- 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있다.
- 템플릿 메소드 패턴은 코드 재사용에 큰 도움이 된다.
- 템플릿 메소드가 들어있는 추상 클래스는 구상 메소드, 추상 메소드, 후크를 정의할 수 있다.
- 추상 메소드는 서브클래스에서 구현한다.
- 후크는 추상 클래스에 들어있는 메소드로 아무 일도 하지 않거나 기본 행동만을 정의한다.
- 서브 클래스에서 후크를 오버라이드 할 수 있다.
- 할리우드 원칙에 의하면, 저수준 모듈을 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋다.
- 템플릿 메소드 패턴은 실전에서 자수 쓰이지만 교과서적인 방식으로 적용되진 않는다.
- 전략 패턴과 템플릿 메소드 패턴은 모두 알고리즘을 캡슐화하지만, 전략 패턴은 구성을, 템플릿 메소드 패턴은 상속을 사용한다.
- 팩토리 메소드 패턴은 특화된 템플릿 메소드 패턴이다.