[Behavioral Patterns] - Template Method

Lee Jeong Min·2022년 1월 23일
0

디자인 패턴

목록 보기
22/23
post-thumbnail

의도

템플릿 메서드는 슈퍼클래스에서 알고리즘의 골격을 정의하지만 하위 클래스가 구조를 변경하지 않고 알고리즘의 특정 단계를 재정의 할 수 있도록 하는 동작 설계 패턴이다.

문제

회사 문서를 분석하는 데이터 마이닝 애플리케이션을 만든다고 해보자. 사용자가 앱 문서를 다양한 형식(PDF, DOC, CSV)로 공급하고, 이러한 문서에서 의미 있는 데이터를 균일한 형식으로 추출하려고 한다.

처음에 만들때 각 버전마다 한 가지의 형식을 지원하게 만들었다고 해보자. 이렇게 되면 데이터 마이닝 클래스에는 중복된 코드가 많이 포함될 것이다.

데이터 마이닝 클래스에는 중복된 코드가 많이 포함되어 있었다.

어느 순간 세 클래스 모두 유사한 코드가 많다는 것을 알게되어, 다양한 데이터 형식을 다루는 코드는 모든 클래스에서 완전히 달랐지만 데이터 처리와 분석을 위한 코드는 거의 동일하다. 알고리즘 구조는 그대로 두고 코드 중복을 없애는게 좋지 않을까?

이러한 클래스를 사용한 클라이언트 코드와 관련된 또 다른 문제가 있다. 처리 객체의 등급에 따라 적절한 행동 경로를 선택하는 조건이 많았다. 세 개의 프로세싱 클래스가 모두 공통 인터페이스나 기본 클래스를 가지고 있다면, 클라이언트 코드의 조건을 제거하고 프로세싱 객체에서 메서드를 호출할 때 다형성을 사용할 수 있다.

해결책

템플릿 메서드 패턴은 알고리즘을 일련의 단계로 나누고 이러한 단계를 메서드로 전환한다음 단일 템플릿 메서드 내에서 이러한 메서드에 대한 일련의 호출을 수행할 것을 제안한다. 단계들은 추상적이거나 일부 기본 구현이 있을 수 있고, 이 알고리즘을 사용하기 위해 클라이언트는 자체적인 하위 클래스를 제공하고, 모든 추상 단계를 구현하며, 필요하다면 일부 선택적 단계를 재정의해야 한다.

데이터 마이닝 앱에서 이렇나 결과가 어떻게 나올지 살펴보자. 우리는 세 가지 구문 분석 알고리즘 모두에 대한 기본 클래스를 만들고 이 클래스는 다양한 문서 처리 단계에 대한 일련의 호출로 구성된 템플릿 메서드를 정의한다.

템플릿 메서드는 알고리즘을 여러 단계로 분할하여 하위 클래스가 이러한 단계를 재정의할 수 있지만 실제 메서드는 재정의할 수 없다.

처음에 모든 단계를 추상적으로 선언할 수 있고, 하위 클래스가 이러한 방법을 위한 자체 구현을 제공하도록 강제한다. 이 경우 하위 클래스는 이미 필요한 모든 구현을 가지고 있으므로 슈퍼 클래스의 메서드에 맞게 메서드의 특징을 조정하기만 하면 된다.

이제 중복된 코드를 제거하기 위해 할 수 있는 것들을 보자. 데이터 형식마다 파일 열기/닫기 코드와 데이터 추출/파싱 코드가 달라 이런 메서드들은 제외하고 원시 데이터 분석 및 보고서 작성과 같은 다른 단계의 구현은 매우 유사하므로 하위 클래스가 해당 코드를 공유할 수 있는 기본 클래스로 끌어올릴 수 있다.

보다시피 2가지 단계가 있다.

  • 추상 단계는 모든 하위 클래스에서 구현되어야 한다.
  • 선택적 단계에는 이미 몇 가지 기본 구현이 있지만 필요한 경우 재정의할 수 있다.

hooks라고 불리는 또다른 단계가 있는데 hook는 본체가 비어 있는 선택적 단계이다. 템플릿 메서드는 hook이 재정의 되지 않은 경우에도 작동하는데, 일반적으로 hook은 알고리즘의 중요한 단계 전후에 배치되어 하위 클래스에 알고리즘에 대한 추가 확장 지점을 제공한다.

현실 유사성

일반적인 아키텍처 계획은 고객의 요구에 맞게 약간 변경될 수 있다.

템플릿 메서드 접근법은 대량 주택 건설에 사용될 수 있다. 표준 주택을 건설하기 위한 건축 계획은 잠재적 소유자가 결과 주택의 세부 사항을 조정할 수 있는 몇 가지 확장 지점을 포함할 수 있다.

건물을 짓는 단계가 조금씩 바뀌어 결과물이 나오는 집이 남들과 조금은 다를 수 있다.

구조

  1. 추상 클래스는 알고리즘의 단계 역할을 하는 메서드뿐만 아니라 이러한 메서드를 특정 순서로 호출하는 실제 템플릿 메서드도 선언한다. 단계들은 추상적이라고 선언되거나 일부 기본 구현이 있을 수 있다.

  2. 구체적인 클래스는 모든 단계를 재지정할 수 있지만 템플릿 메서드 자체는 재지정할 수 없다.

적용가능성

  • 클라이언트가 알고리즘의 특정 단계만 확장하고 전체 알고리즘 또는 해당 구조를 확장하지 않으려면 템플릿 메서드 패턴을 사용하라.

  • 거의 동일한 알고리즘과 약간의 차이가 있는 여러 클래스가 있는 경우 이 패턴을 사용하라. 그 결과로, 알고리즘이 변경될때 모든 클래스를 수정해야 할 수 있다.

장단점

장점

  • 클라이언트가 대형 알고리즘의 특정 부분만 재정의하도록 하여 알고리즘의 다른 부분에 발생하는 변경 사항의 영향을 덜 받게 할 수 있다.
  • 중복 코드를 슈퍼클래스로 끌어올 수 있다.

단점

  • 일부 클라이언트는 제공된 알고리즘 골격에 의해 제한될 수 있다.

  • 하위 클래스를 통한 기본 단계 구현을 억제하여 Liskov 치환 원칙을 위반할 수 있다.

    리스코프 치환 원칙: 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야한다는 것

  • 템플릿 메서드는 단계가 많을수록 유지하기가 더 어려운 경향이 있다.

Template Method in TypeScript

TypeScript의 패턴 사용

복잡도: ★☆☆

인기: ★★☆

사용 예: 템플릿 메서드 패턴은 TS 프레임워크에서 일반적이다. 개발자들은 종종 상속을 사용하여 표준 기능을 확장하는 간단한 방법을 프레임워크 사용자에게 제공하기 위해 이 기능을 사용한다.

식별: 템플릿 메서드는 기본 클래스에서 정의한 '기본' 동작이 이미 있는 동작 메서드에서 인식될 수 있다.

index.ts

// 추상 클래스는 원시 연산을 추상화하는 호출로 구성된
// 일부 알고리즘의 골격을 포함하는 템플릿 메서드를 정의한다.
abstract class AbstractClass {
  public templateMethod(): void {
    this.baseOperation1();
    this.requiredOperations1();
    this.baseOperation2();
    this.hook1();
    this.requiredOperation2();
    this.baseOperation3();
    this.hook2();
  }

  // 이미 구현되어 있는 기능들
  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');
  }

  // 하위 클래스에서 구현될 것들
  protected abstract requiredOperations1(): void;

  protected abstract requiredOperation2(): void;

  // 훅들
  protected hook1(): void {}

  protected hook2(): void {}
}

class ConcreteClass1 extends AbstractClass {
  protected requiredOperations1(): void {
    console.log('ConcreteClass1 says: Implemented Operation1');
  }

  protected requiredOperation2(): void {
    console.log('ConcreteClass1 says: Implemented Operation2');
  }
}

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');
  }
}

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());

결과

요약

템플릿 메서드는 슈퍼클래스에서 알고리즘의 골격을 정의하는데 하위 클래스엥서 구조를 변경하지 않고 알고리즘의 특정 단계를 재정의할 수 있도록 해준다.

이를 통해 중복적인 코드를 슈퍼클래스에 두고, 수행작업이 거의 동일한 알고리즘을 하위클래스에서 재정의하여 사용할 수 있다.

참고 사이트

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글