Published 2021. 12. 29. 10:03
반응형

템플릿 메서드 패턴이란?


실행 과정/단계는 동일한데 각 단계 중 일부의 구현이 다른 경우 사용할 수 있는 패턴이다.
동일한 실행 과정을 제공하는 템플릿 메서드를 활용해 중복을 제거할 수 있다.

크게 두 가지 구조로 나뉜다.

  1. 실행 과정을 구현한 상위 클래스
  2. 일부 단계를 구현한 하위 클래스

상위 타입의 역할은 무엇일까?


  1. 실행 과정을 구현한 메서드를 제공하는 역할을 한다.
  2. 일부 단계 구현을 추상 메서드로 제공한다. 이 일부 단계 구현은 각 다른 구현이기 때문에 추상 메서드로 제공하며, 하위 클래스에서 구현하게 된다.
  3. 실행 과정 메서드에서 일부 단계 구현 메서드를 호출하는 방식이다.
  4. 보통은 하위 타입이 상위 타입의 기능을 재사용할지 정하기 때문에 흐름의 주체가 하위이지만 템플릿 메서드 패턴은 흐름의 주체가 상위 타입이다.

하위 타입의 역할은 무엇일까?


하위 타입은 상위 타입에서 제공한 추상 메서드를 구현하는 일만 할 뿐 다른 것은 하지 않는다.
애초에 동일한 코드에서 약간 다른 부분 추출을 위한 타입이기 때문이다.

템플릿 메서드란?


템플릿 메서드는 상위 클래스에서 제공하는 실행 과정을 구현한 메서드를 의미한다.
하위 클래스는 동일한 구현이 아니끼 때문에 템플릿이 아니고, 동일한 기능을 제공하는 상위 타입의 메서드가 템플릿 메서드인 것이다.

실제 패턴을 코드로 살펴 보자.


아래 코드는 DB 데이터와 LDAP를 이용해서 인증 처리하는 클래스이다.
과정 자체는 완전히 동일하다. 하지만 구현이 다른 상황이다.

개선 전 코드를 보자.

DB 데이터 이용

public class DbAuthenticator {

      public Auth authenticate(String id, String pw) {
          // 사용자 정보로 인증 확인
          User user = userDao.selectById(id);
          boolean auth = user.equalPassword(pw);

          // 인증 실패 시 예외
          if (!auth) {
              throw createException();
        }

          // 인증 성공 시, 인증 정보 제공
          return new Auth(id, user,getName());
    }

      private AuthException createException() {
          return new AuthException();
    }
}

LDAP 이용

public class LDAPAuthenticator {

      public Auth authenticate(String id, String pw) {
          // 사용자 정보로 인증 확인
          boolean auth = ldapClient.authenticate(id, pw);

          // 인증 실패 시 예외
          if (!auth) {
              throw createException();
        }

          // 인증 성공 시, 인증 정보 제공
                LdapContext ctx = ldapClient.find(id);
                return new Auth(id, ctx.getAttribute("name");
    }

      private AuthException createException() {
          return new AuthException();
    }
}

위의 두 코드를 비교해보면 실행 과정이 완전히 동일하다. 다만 구현이 다를 뿐이다.
이제 과정을 상위 타입으로 옮기고, 구현을 하위 타입으로 옮겨서 템플릿 메서드 패턴으로 개선해 보자.

템플릿 메서드 패턴으로 개선 해보자.

개선 전 코드와 주석을 비교해 보면 쉽게 확인할 수 있다.

실행 과정은 템플릿 메서드에서 실행을 하고, 그 안에서 각 단계의 구현인 추상 메서드를 호출하고 있다.
이와 같이 authenticate메서드는 모든 하위 타입에 동일하게 적용되는 실행 과정(템플릿)을 제공하기 때문에 템플릿 메서드라고 부르는 것이다.

public abstract Authenticator {

      // 템플릿 메서드
      public Auth authenticate(String id, String pw) {
          // 이 부분이 과정이다.
        // 과정에서 구현이 다른 추상 메서드를 호출 한다.
          // 사용자 정보로 인증 확인, 인증 실패 시 예외
          if (!doAuthenticate(id, pw)) { // 추상 메서드 호출
              throw createException();
        }
          // 인증 성공 시, 인증 정보 제공
          return createAuth(id); // 추상 메서드 호출
    }

      protected abstract boolean doAuthenticate(String id, String pw);
      protected abstract Auth createAuth(String id);

      private AuthException createException() {
          throw new AuthException():
    }
}

추상 메서드가 protected인 이유

외부에서 호출되는 인터페이스 오직 템플릿 메서드이고, 추상 메서드는 하위 클래스에서 재정의용으로만 사용이 되어야 하기 때문에
public이 아닌 protected이다.

그럼 이제 하위 클래스인 LDAP 구현 클래스는 추상 메서드만 구현을 하면된다.
실행 과정 코드는 전혀 없고, 오직 구현 코드만 있다.

public class LdapAuthenticator extends Authenticator {

      @Override
      protected boolean doAuthenticate(String id, String pw) {
          return ldapClinet.authenticate(id, pw);
    }

        @Override
        protected Auth createAuth(String id) {
                return .....
        }
}

장점

코드의 중복을 제거할 수 있다. 새로운 방식이 추가 되어도 과정은 동일할텐데 중복된 코드가 출현하게 되면
유지보수가 어려운 코드가 된다. 하지만 템플릿 메서드 패턴으로 중복을 제거할 수 있게 된다.

흐름의 주체가 상위 타입이다.


위의 코드를 보면 템플릿 메서드에서 모든 흐름을 제어하고 있다. 보통 하위 타입에서 흐름을 제어하는 것을 많이 보았을 것이다.

아래 코드처럼 상속을 받는 다는 것은 재사용을 하는 것이기 때문에 상위 타입의 메서드를 하위 타입에서 재사용할지 말지 흐름을 제어하는 것이다.
하지만 템플릿 메서드 패턴은 상위 타입에서 하위 타입의 추상 메서드를 호출하는 방식으로 흐름을 제어하게 된다.

public class SuperCar extends ZetEngine {

        @Override
        public void turnOn() {
                // 하위 클래스에서 흐름 제어
                if (notReady) {
                        beep();
                } else {
                        super.turnOn() {
                }
        }
}

정리


  • 실행 과정은 같으나 구현이 다른 경우 이를 템플릿으로 제공하여 중복을 제거하기 위한 패턴이다.
  • 흐름의 주체가 상위 타입이다.

참고 자료


개발자가 반드시 정복해야할 객체지향과 디자인패턴 p.182

반응형

'기타 IT' 카테고리의 다른 글

@ParameterizedTest를 언제 사용해야 할까?  (0) 2022.03.03
상태 패턴이란  (0) 2022.01.05
전략(Strategy) 패턴  (0) 2021.12.23
디미터 법칙과 Tell, Don't Ask 법칙  (3) 2021.12.10
POSIX의 EOF(End Of File) 규칙  (0) 2021.12.03
복사했습니다!