알고리즘의 골격을 수퍼클레스에서 정의하지만 서브클래스가 구조를 바꾸지 않으면서
알고리즘의 특정 단계를 override하는 행동 패턴
기업 문서를 분석하는 데이터 마이닝 앱 가정
템플릿 메서드 패턴 - 알고리즘을 일련의 단계로 나눠 각 단계를 메서드로 바꾸고 단일 템플릿 메서드에서 이 메서드들에 일련의 호출을 보냄
위 예제
1. 추상 클래스 - 알고리즘의 단계 역할을 하는 메서드들과, '
이 메서드들을 특정 순서로 호출하는 실제 템플릿 메서드 선언
- 각 단계들은 추상으로 선언되거나 default 구현을 가질 수 있음
2. concrete 클래스 - 템플릿 메서드 자체를 제외한 모든 단계를 override 할 수 있음
- 모놀리식 알고리즘을 수퍼클래스에서 구조를 그대로 유지하면서
서브클래스들로 쉽게 확장될 수 있는 일련의 단계들로 나눔
- 알고리즘을 템플릿 메서드로 바꾸면 비슷한 구현을 가지는 단계들을
수퍼클래스에 넣어 코드 중복을 없앨 수 있음
1. 단계들로 나눌 수 있는 타겟 알고리즘 분석
- 어떤 단계들이 모든 서브클래스들에 공통적이고 어떤 단계들이 항상 고유할 지 고려
2. 추상 기초 클래스를 생성하고 템플릿 메서드와 알고리즘의 단계들을 표현하는
추상 메서드들의 집합 선언
- 템플릿 메서드에서 대응되는 단계들을 실행해 알고리즘 구조의 윤곽을 잡음
- 템플릿 메서드를 final로 만들어 서브클래스들이 override 하지 못하게 하는 걸 고려
3. 모든 단계들이 추상이여도 괜찮지만 특정 단계들을 기본 구현을 가짐으로서 이득을 볼 수 있음
- 서브클래스들은 이 메서드들을 구현하지 않아도 됨
4. 알고리즘의 필수적인 단계들 사이에 hooks를 추가하는 것을 고려
5. 알고리즘의 각 변형마다 새 concrete 서브클래스 생성
- 모든 추상 단계들을 구현해야 하고, 선택적 단계들을 override 해도 됨
- 클라이언트가 큰 알고리즘의 특정 부분들을 override 할 수 있게 해,
알고리즘의 다른 부분들에서 발생하는 변화에 영향을 덜 받게 할 수 있음
- 중복 코드를 수퍼클래스로 옮길 수 있음
- 몇몇 클라이언트들은 주어진 알고리즘의 골격에 의해 제한될 수 있음
- 서브클래스를 통한 default 단계 구현을 제한해 LSP 위반
- 단계가 많을수록 템플릿 메서드 유지가 어려움
- 팩토리 메서드 - 템플릿 메서드의 특수화,
팩토리 메서드는 큰 템플릿 메서드의 한 단계 역할을 할 수 있음
- 템플릿 메서드 패턴 - 상속 기반, 알고리즘의 부분들을 서브클래스에서 확장해 변경,
클래스 레벨에서 동작해 정적임
전략 패턴 - 합성 기반, 객체 행동의 부분들을 다른 전략을 제공해줌으로써 변경,
객체 레벨에서 동작해 런타임에 행동들을 변경
/**
* The Abstract Class defines a template method that contains a skeleton of some
* algorithm, composed of calls to (usually) abstract primitive operations.
*
* Concrete subclasses should implement these operations, but leave the template
* method itself intact.
*/
abstract class AbstractClass {
/**
* The template method defines the skeleton of an algorithm.
*/
public templateMethod(): void {
this.baseOperation1();
this.requiredOperations1();
this.baseOperation2();
this.hook1();
this.requiredOperation2();
this.baseOperation3();
this.hook2();
}
/**
* These operations already have implementations.
*/
protected baseOperation1(): void {
console.log('AbstractClass says: I am doing the bulk of the work');
}
protected baseOperation2(): void {
console.log('AbstractClass says: But I let subclasses override some operations');
}
protected baseOperation3(): void {
console.log('AbstractClass says: But I am doing the bulk of the work anyway');
}
/**
* These operations have to be implemented in subclasses.
*/
protected abstract requiredOperations1(): void;
protected abstract requiredOperation2(): void;
/**
* These are "hooks." Subclasses may override them, but it's not mandatory
* since the hooks already have default (but empty) implementation. Hooks
* provide additional extension points in some crucial places of the
* algorithm.
*/
protected hook1(): void { }
protected hook2(): void { }
}
/**
* Concrete classes have to implement all abstract operations of the base class.
* They can also override some operations with a default implementation.
*/
class ConcreteClass1 extends AbstractClass {
protected requiredOperations1(): void {
console.log('ConcreteClass1 says: Implemented Operation1');
}
protected requiredOperation2(): void {
console.log('ConcreteClass1 says: Implemented Operation2');
}
}
/**
* Usually, concrete classes override only a fraction of base class' operations.
*/
class ConcreteClass2 extends AbstractClass {
protected requiredOperations1(): void {
console.log('ConcreteClass2 says: Implemented Operation1');
}
protected requiredOperation2(): void {
console.log('ConcreteClass2 says: Implemented Operation2');
}
protected hook1(): void {
console.log('ConcreteClass2 says: Overridden Hook1');
}
}
/**
* The client code calls the template method to execute the algorithm. Client
* code does not have to know the concrete class of an object it works with, as
* long as it works with objects through the interface of their base class.
*/
function clientCode(abstractClass: AbstractClass) {
// ...
abstractClass.templateMethod();
// ...
}
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass1());
console.log('');
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass2());
// Output.txt
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
참고 자료: Refactoring.guru