템플릿 메소드 패턴
템플릿 메소드 패턴
- 알고리즘의 골격을 변경하지 않고 하위 클래스에서 알고리즘의 일부 단계를 재정의할 수 있도록 해줌
여러 클래스들이 비슷한 알고리즘 구조를 가지고있지만 서로 약간식 다른경우 AbstractClass로 골격을 만들어 ConcreteClass에서 재정의 하여 쓰도록 한다.
- 상위 클래스의 탬플릿 메소드에 Concrete Method으로 알고리즘이 기술되어 있으므로 코드 중복을 줄일 수 있음, abstract , final 을 이용하여 하위 클래스가 할 수 있는 일을 제한할 수 있음
전략 패턴은 알고리즘 전체를 바꿀 때 사용
탬플릿 메소드 패턴은 알고리즘의 틀은 유지한 상태에서 하위 클래스에서 일부 단계만 구체화함
템플릿 메소드 패턴 구조
|
primitiveMethod = abstractMethod
AbstractClass에 알고리즘 골격을 정의한 Template 메소드가 구현되어있다.
AbstractClass(상위)를 상속받아 알고리즘에서 쓰이는 메서드들을 ConcreteClass(하위)에서 구체화한다.
같은 알고리즘 골격이지만 기능이 약간씩 다른 경우 활용할수있다.
Template 메소드 패턴 구성
//AbstractClass: Template 메소드가 구현된 최상위 추상 클래스
public abstract class CaffeineBeverage{
//Concrete Method: public final , private: 하위 재정의 불가
public final void prepareRecipe(){ //<-Template 메소드:알고리즘 골격 정의
boilWater();
brew();
pourInCup();
if(customerWantsCondiments())
addCondiments();
}
//Abstract Method: protected, public: 하위 필수적 재정의 : 알고리즘에 쓰이는 메서드들 하위 클래스에게 정의 위임
protected abstract void boilWater();
protected abstract void brew();
protected abstract void pourInCup();
protected abstract void addCondiments();
//Hook Method: protected, public: 하위 선택적 재정의 : 상위에 정의해두고 하위에서 선택적으로 재정의하도록 함
public boolean customerWantsCondiments(){
return true;
}
}
템플릿 메서드 패턴은 다음 3가지 메서드를 활용한다.
Concrete Method: Template 메소드가 정의된 클래스에 완전히 정의되는 메소드로 하위 클래스에서는 재정의하지 않아야 하는 연산
Abstract Method: Template 메소드가 정의된 클래스에 선언만 되어 있는 추상 메소드로 하위 클래스에서 반드시 재정의하여야 하는 연산
Template 메소드 내에 abstract 메소드로 정의되는 단계가 너무 많으면 하위 클래스에게 부담될 수 있음
public 메소드가 아니라 내부적으로만 사용하는 메소드일 수 있음 자식에서 재정의해야 하는 경우에는 최소 protected로 설정되어 있어야 함
Hook Method: Template 메소드가 정의된 클래스에 기본 행동이 정의되어 있는 메소드로 하위 클래스에서 선택적으로 재정의할 수 있는 연산
하위 클래스에서 어떤 메소드를 반드시 구현해야 하는지 알 수 없음. 따라서 문서화가 매우 중요함
알고리즘의 골격을 유지하고 하위 클래스에서 알고리즘의 일부 단계를 재정의할 수 있도록 해줌
//ConcreteClass01: AbstractClass를 구체화 하고있는 하위 클래스: 여러개가 존재한다.
public class Coffee extends CaffeineBeverage{
Abstract Methods 필수 overriding..
Hook Methods 선택적 overriding..
}
//ConcreteClass02 ... ConcreteClass n
알고리즘이 유사하지만 서로다른 다양한 자손(ConcreteClass)들을 구현 할수있다.
Client 적용
//Cafe: Template 메소드를 구현하고있는 클래스를 이용하여 클라이언트에게 기능을 제공하는 클래스
public class Cafe{
private CaffeineBeverage cb=null;
public void setCaffeineBeverage(CaffeineBeverage cb){ //적용할 하위 클래스를 주입
this.cb = cb;
}
public void prepare(){
if(cb!=null) cb.prepareRecipe();
}
}
//Client(TestCode)
public static void main(String[] args) {
Cafe cafe1 = new Cafe();
cafe1.setCaffeineBeverage(new Coffee()); //AbstractClass에게 ConcreteClass 주입
cafe.prepareRecipe(); //주입되는 ConcreteClass에 따라 AbstractClass의 Template method의 결과가 다르게 나온다.
}
효과
의존성 부패(dependency rot) 문제를 방지할 수 있음 의존성 부패는 상위 수준 요소가 하위 수준 요소에 의존하고 이 하위 수준 요소가 다시 상위 수준 요소에 의존하는 등 의존 관계가 복잡하고 꼬여 있는 경우를 말함: 양방향 의존관계
상위, 하위 수준 요소는 무조건 상속 관계를 말하는 것은 아님 Has-a 관계에서도 상위 하위 가 있다.
구체적인 기능을 수행하면 하위 수준 요소라고 한다. : DI될 구체 클래스는 하위수준요소
하위 수준 요소들을 이용하여 기능을 수행하면 상위 수준 요소라고 한다. : has-a 보유 클래스는 상위 수준
Applicability
메소드를 위한 골격을 제공하여 자식 클래스들이 메소드의 특정 부분들만 재정의하도록 해야 할 때
모든 자식 클래스에 공통적으로 정의되는 메소드이지만 약간씩 차이가 있는 경우
자식 클래스가 반드시 재정의해야 하는 메소드를 제어하기 위해
이점
상위 클래스의 탬플릿 메소드에 알고리즘이 기술되어 있으므로 코드 중복을 줄일 수 있음
하위 클래스가 할 수 있는 일을 제한할 수 있음
자식 클래스의 확장을 제한할 수 있음
단점
상속을 이용하기때문에 알고리즘의 고정된 골격에 의해 유연성이 떨어질 수 있음
is-a 관계 대신에 has-a 관계로 모델링할 수 있음 (전략 패턴 형태)
추상 메소드는 전략에 해당하는 객체 메소드를 호출하는 형태로 구현함
이 방법에서는 훅 메소드를 활용할 수 없음
하위 클래스 이름을 통한 구분이 가능하지 않음
전략 패턴과 비교
전략 패턴은 알고리즘 전체를 바꿀 때 사용
탬플릿 메소드 패턴은 알고리즘의 틀은 유지한 상태에서 하위 클래스에서 일부 단계를 구체화함
Factory 메소드 패턴
전형적인 template 메소드 패턴임 생성하는 부분만 추상 메소드임