본문 바로가기
source-code/software

Template Method pattern

by mattew4483 2023. 8. 20.
728x90
반응형

배경

결제 관련된 페이지를 만드는 도중 이런 상황과 마주했다.

  • 결제(이하 멤버십)에는 세 가지 단계가 존재(베이식, 스탠다드, 프리미엄)
  • 각 결제 단계마다 1) 해당 단계의 이미지 2) 해당 단계의 문구 3) 해당 단계 신청 api 가 존재

위와 같은 상황에서 베이직, 스탠다드, 프리미엄 멤버십 객체는 어떻게 구성되어야 할까?

class Membership {
}

class Basic extends Membership {
    image  = require('basic.png')
    description = '배이직 멤버십 입니다'

    apply() {
    // 베이직 멤버십 신청 함수
    }
}

class Standard extends Membership {
    image  = require('standard.png')
    description = '스탠다드 멤버십 입니다'

    apply() {
    // 스탠다드 멤버십 신청 함수
    }
}

각 멤머십 단계는 분명 Membership이라는 Class를 상속받는 형태여야 할 텐데...
상속받은 각 객체인 Basic, Standard, Premium은 각 속성과 메서드가 비슷하면서도 약간의 차이가 존재하는 상황이다!
 
흠... 그렇다면 각 멤버십 단계가 상속받는 Membershipd이란 Class에는 아무것도 정의할 수가 없는 걸까?
각 멤버십 단계들은 분명 공통적인 알고리즘 구조를 갖고 있는데, 이를 명시적으로 표현할 방법은 없을까?
 

Template Method pattern

이러한 상황에서 Template Method 패턴을 사용할 수 있다.

Template Method 패턴은 알고리즘의 골격을 정의하고 세부 구현을 서브클래스에 위임하는 패턴입니다. 
공통의 알고리즘 구조를 정의하고 각 단계의 구체적인 구현을 서브클래스에서 제공합니다.
상위 클래스는 추상 메서드와 구체 메서드를 포함하며, 추상 메서드는 서브클래스에서 오버라이딩해야 합니다.
주로 알고리즘의 일부분이 변할 때 사용됩니다.

아하!
즉 각 멤버십이 공통적으로 갖는 알고리즘 구조는 상위 클래스에서 정의하고
상위 클래스에서 정의된 추상 메서드는 서브 클래스에서 구체적으로 구현해 사용한다면
→ 각 하위 클래스 별로 속성과 메서드를 정의하는 동시에, 상위 클래스를 통해 각 객체들의 공통된 알고리즘을 명확하게 표현할 수 있겠다!
 
특히 위 상황에서는 상위 클래스를 구현한 서브 클래스(Basic, Standard, Premium)의 인스턴스만 존재할 뿐,
상위 클래스인 Membership을 인스턴스화할 필요가 없으므로
→ 이를 추상 클래스로 구현한다면 의도를 좀 더 명시적으로 전달할 수 있을 테다.
 

// 상위 Class인 Membership을 추상 Class로 작성
abstract class Membership {
  constructor(
    protected image: string,
    protected description: string
  ) {}

  abstract apply(): void; // 추상 메서드 -> 반드시 서브 클래스에서 구현
}

Membership은 '멤버십' 객체가 가져야 할 구조만 정의한 채, 구체적인 구현은 서브 클래스에 위임할 수 있도록 작성했다.
 

// 서브 클래스에서, 각 객체에 맞는 로직 구현
class Basic extends Membership {
  constructor() {
    super('basic-image.jpg', 'Basic Membership Description');
  }

  apply(): void {
    // Basic Membership에 특화된 제출 로직을 구현
  }
}

class Standard extends Membership {
  constructor() {
    super('standard-image.jpg', 'Standard Membership Description');
  }

  apply(): void {
    // Standard Membership에 특화된 제출 로직을 구현
  }
}

class Premium extends Membership {
  constructor() {
    super('premium-image.jpg', 'Premium Membership Description');
  }

  apply(): void {
    // Premium Membership에 특화된 제출 로직을 구현
  }
}

각 서브 클래스에서 추상 메서드를 오버라이딩해, 각 멤버십 별 동작을 구현한 모습!

728x90
반응형