S E P H ' S

[행동 패턴] 템플릿 메소드(Template Method) 패턴 본문

Programing & Coding/Design Pattern

[행동 패턴] 템플릿 메소드(Template Method) 패턴

yoseph0310 2023. 3. 19. 17:22

템플릿 메소드(Template Method) 패턴

템플릿 메소드 패턴이란?

 

GoF의 디자인 패턴에 의하면, 템플릿 메소드 패턴을 아래와 같이 정의한다.

 

알고리즘의 구조를 메소드에 정의하고,
하위 클래스에서 알고리즘 구조의 변경없이 알고리즘을 재정의 하는 패턴이다.
알고리즘이 단계별로 나누어지거나,
같은 역할을 하는 메소드이지만 여러곳에서 다른 형태로 사용이 필요한 경우 유용한 패턴이다.

 

토비의 스프링에서는 아래와 같이 정의하고 있다.

상속을 통해 슈퍼클래스의 기능을 확장할 때 사용하는 가장 대표적인 방법.
변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다.

 

언제 사용할까?

 

  • 두 개 이상의 프로그램이 기본적으로 동일한 골격 하에서 동작할 때 기본 골격에 해당하는 알고리즘은 일괄적으로 관리하면서 각 프로그램마다 달라지는 부분들에 대해서는 따로 만들고 싶을 때

 

구조

 

  • hook 메소드 : 추상 클래스에서 선언하고 기본적인 내용만 구현하거나 비워놓는 메소드. 하위 클래스에서 오버라이딩하면 다양한 용도로 사용할 수 있다.

 

장단점

 

장점

 

  • 중복코드를 줄일 수 있다.
  • 자식 클래스의 역할을 줄여 핵심 로직의 관리가 용이함.
  • 코드를 보다 더 객체지향적으로 구성할 수 있다.

 

단점

 

  • 추상 메소드가 많아지면서 클래스 관리가 복잡해짐
  • 클래스간의 관계와 코드가 꼬여버릴 염려가 있다.

 

예시

 

선생님이 수업을 하는 과정을 템플릿 메소드의 예시로 코드를 작성해보겠다. 국어선생님, 수학선생님, 영어선생님들 모두 교실로 들어와서 출석을 부르고 수업을 하고 강의실을 나간다. 하지만 수업을 하는 과목은 다르다.

여기서 공통적으로 구성해야할 것은 

  1. 교실에 들어온다.
  2. 출석을 부른다.
  3. 강의실을 나간다.

먼저 이 세가지를 구현하고, 하위 클래스에서 각각 다른 과목(국어, 수학, 영어)에 대한 기능을 추상메소드를 상속받아 정의한다.

 

public abstract class Teacher {
    public void startClass() {
        inside();
        attendance();
        outside();
        teach();
    }

    // 공통 메소드
    public void inside() {
        System.out.println("교실로 들어온다.");
    }

    public void attendance() {
        System.out.println("출석을 부른다.");
    }

    public void outside() {
        System.out.println("교실을 나간다.");
    }

    // 추상 메소드
    public abstract void teach();
}

공통 기능과 다르게 작동하게 할 추상 메소드를 정의한 추상클래스인 Teacher 클래스를 만든다.

 

public class KoreanTeacher extends Teacher {

    @Override
    public void teach() {
        System.out.println("국어 수업 하기");
    }

}

 

public class MathTeacher extends Teacher {
    @Override
    public void teach() {
        System.out.println("수학 수업 하기");
    }
}

 

public class EnglishTeacher extends Teacher {
    @Override
    public void teach() {
        System.out.println("영어 수업 하기");
    }
}

 

각각 teach라는 추상메소드를 다른 방식으로 구현한다.

 

테스트

 

public class templateMethodTest {
    public static void main(String[] args) {
        KoreanTeacher krTeacher = new KoreanTeacher();
        MathTeacher mathTeacher = new MathTeacher();
        EnglishTeacher enTeacher = new EnglishTeacher();

        krTeacher.startClass();
        System.out.println("**** 1교시 끝 ****");
        mathTeacher.startClass();
        System.out.println("**** 2교시 끝 ****");
        enTeacher.startClass();
        System.out.println("**** 3교시 끝 ****");
    }
}

 

 

공통 기능에 대해서는 상속받아 사용하고 다르게 작동해야할 기능에 대해서는 추상 메소드를 자식클래스에서 직접 구현하여 사용하는 템플릿 메소드 패턴에 대해 알아보았다. 템플릿 메소드 패턴은 Spring에서도 적용 사례가 있다.

 

Spring 에서 템플릿 메소드 패턴을 적용한 사례

 

Spring에서 템플릿 메소드 패턴은 DispatcherSevlet에서 사용되고 있다.

Spring의 DispatcherServlet은 Http 요청에 대해 처리하는데, DispatcherServlet은 Servlet을 상속받은 것으로 구현되어 있다.

 

정확하게는 DispatcherServlet은 FrameworkServlet을 상속받았다. DispatcherServlet의 doService() 라는 메소드가 있는데 이 메소드는 실질적으로 Http 요청에 대해 처리하는 역할을 하는데 이 메소드가 바로 템플릿 메소드 패턴으로 구현되어 있다.

 

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    
    ......
    
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        ......

        initContextHolders(request, localeContext, requestAttributes);

        try {
            doService(request, response); // template method pattern 이용
        }
        ......
    }
    ......

    protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception; // subClass에게 위임

    ......
}

public class DispatcherServlet extends FrameworkServlet {

    ...

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);

        ......

    }

    ...
}

 

FrameworkServlet에서 doService() 구현은 하위 클래스에 위임하고 있고 DispatcherServlet은 doService()를 구체화하고 있다. 즉, DispatcherServlet을 구현하여 processRequest를 호출하면 FrameworkServlet의 processRequest() 의 로직을 수행하다가 doService(request, response)를 만나면 DispatcherServlet의 로직을 수행하는 것이다.

 


Spring 사용예시 출처 : https://steady-coding.tistory.com/585