디자인 패턴(구)/행위 패턴

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

공대키메라 2022. 4. 29. 00:07

1. 템플릿 메소드 패턴이란?

의도

객체의 연산에는 알고리즘의 뼈대만을 정의하고 각 단계에서 수행할 구체적 처리는 서브클래스 쪽으로 미룬다. 알고리즘의 구조 자체는 그대로 놔둔 채 알고리즘 각 단계 처리를 서브클래스에서 재정의할 수 있게 한다. 

 

즉, 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법이다.

사용 시기

  • 어떤 한 알고리즘을 이루는 부분 중 변하지 않는 부분을 한 번 정의해 놓고 다양해질 수 있는 부분은 서브클래스에서 정의할 수 있도록 남겨두고자 할 때
  • 서브클래스 사이의 공통적인 행동을 추출하여 하나의 공통 클래스에 몰아둠으로써 코드 중복을 피하고 싶을 때
  • 서브클래스의 확장을 제어하고자 할 때

구조

  • Abstract Class : 서브클래스들이 재정의를 통해 구현해야 하는 알고리즘 처리 단계 내의 기본연산을 정의 
  • ConcreteClass : 서브클래스마다 달라진 알고리즘 처리 단계를 수행하기 위한 기본연산을 구현

2. 구현

다른 예제가 여러개 많았는데 필자는 그럼에도 불구하고 직접 만들어 보고 싶었다.

이름하여 키메라의 엉터리 계산기!

코드 기능이 이상하거나 그런것에 집중하지 말고 

중요한 것은 결국 이것이 의도를 잘 따르고 있는지이다. 

 

필자는 다음을 구현할 것이다.

Calculator.java

import java.util.HashMap;
import java.util.Map;

public abstract class Calculator {

    private Map<String, Integer> map = new HashMap<>();

    public void makeProcess(String calName, int a, int b){
        if(map.containsKey(calName)){
            System.out.println("이미 존재하는 방식입니다.");
            for (String s : map.keySet()) {
                System.out.println("map key : " + calName + "/ map value : " + map.get(s));
            }
        }else{
            System.out.println("공식 값 세팅하기");
            System.out.println("공식 이름 : " + calName);
            System.out.printf("입력값 : %d & %d\n", a, b);
            int result = getResult(a, b);
            System.out.println("결과값 : " + result);
            map.put(calName, result);
        }
        System.out.println();
    }

    public void executeProcess(String calName){
        if(map.containsKey(calName)){
            System.out.println("공식 찾아보기 : " + calName);
            System.out.println("결과 : " + map.get(calName));
        }else {
            System.out.println("존재하지 않는 공식입니다. 실패!");
        }
        System.out.println();
    }

    protected abstract int getResult(int a, int b);

}

Plus.java

public class Plus extends Calculator{
    @Override
    protected int getResult(int a, int b) {
        return a + b;
    }
}

DoublePlus.java

public class DoublePlus extends Calculator {
    @Override
    protected int getResult(int a, int b) {
        return a+a+b+b;
    }
}

Main.java

public class Main {

    public static void main(String[] args) {
        Calculator calculator = new Plus();
        calculator.makeProcess("그냥더하기",1,2);
        calculator.executeProcess("그냥더하기");
        calculator.executeProcess("아무거나 입력하기");

        Calculator calculator1 = new DoublePlus();
        calculator.makeProcess("따블로 더하기1", 2, 2);
        calculator.executeProcess("따블로 더하기1");
        calculator1.makeProcess("따블로 더하기2", 4,4);
        calculator1.executeProcess("따블로 더하기2");
    }
}

 

결과값이 궁금하면 (500원) 클릭!

더보기
공식 값 세팅하기
공식 이름 : 그냥더하기
입력값 : 1 & 2
결과값 : 3

공식 찾아보기 : 그냥더하기
결과 : 3

존재하지 않는 공식입니다. 실패!

공식 값 세팅하기
공식 이름 : 따블로 더하기1
입력값 : 2 & 2
결과값 : 4

공식 찾아보기 : 따블로 더하기1
결과 : 4

공식 값 세팅하기
공식 이름 : 따블로 더하기2
입력값 : 4 & 4
결과값 : 16

공식 찾아보기 : 따블로 더하기2
결과 : 16

 

더 간단하게 할 수 있지만 쓸데없이 별스럽지 않은 기능을 추가해서 사용해보고 싶었다. 심심해...

 

과연 위의 코드가 우리가 처음 본 구조에 대응되는지 확인해보겠다.

 

구조

  • Abstract Class : 서브클래스들이 재정의를 통해 구현해야 하는 알고리즘 처리 단계 내의 기본연산을 정의 
  • ConcreteClass : 서브클래스마다 달라진 알고리즘 처리 단계를 수행하기 위한 기본연산을 구현

 

Abstract Class는 위에서 Calculator에 대응된다. Calculator에서는 서브클래스들의 기본 연산을 정의한다.

 

ConcreteClass에 해당되는 class는 Plus와 DoublePlus다. 

 

Abstract Class를 구현하는데 기본연산 외에 일부분만 재정의를 통해 새로운 기능을 만들 수 있다.

 

즉, 의도에서 봤듯이 알고리즘 각 단계 처리를 서브클래스에서 재정의한 것이다!

3. 템플릿 콜백 패턴(Template Callback) 패턴

구조

잠시 템플릿 메서드 패턴과 매우 유사한 템플릿 콜백 패턴에 대해 알아보려고 한다. 

 

위의 템플릿 메서드 패턴은 추상클래스로 구현해서 상속을 사용했다. 

 

하지만 이 구조를 사용하면 상속을 하지 않아도 된다. 

 

위임을 사용함으로서 구현체를 만드는 것보다 익명 내부 클래스 혹은 람다 표현식을 이용해 적용이 가능하다. 

 

그렇게 되면 상속보다는 위임을 사용하며 코드가 좀 더 간결해질 수 있다. 

CalculatorForCallback.java

import java.util.HashMap;
import java.util.Map;

public class CalculatorForCallback {

    private Map<String, Integer> map = new HashMap<>();

    public void makeProcess(String calName, int a, int b, CallbackInterface callbackInterface){
        if(map.containsKey(calName)){
            System.out.println("이미 존재하는 방식입니다.");
            for (String s : map.keySet()) {
                System.out.println("map key : " + calName + "/ map value : " + map.get(s));
            }
        }else{
            System.out.println("공식 값 세팅하기");
            System.out.println("공식 이름 : " + calName);
            System.out.printf("입력값 : %d & %d\n", a, b);
            int result = callbackInterface.getResult(a, b);
            System.out.println("결과값 : " + result);
            map.put(calName, result);
        }
        System.out.println();
    }

    public void executeProcess(String calName){
        if(map.containsKey(calName)){
            System.out.println("공식 찾아보기 : " + calName);
            System.out.println("결과 : " + map.get(calName));
        }else {
            System.out.println("존재하지 않는 공식입니다. 실패!");
        }
        System.out.println();
    }

}

CallbackInterface.java

public interface CallbackInterface {
    int getResult(int a, int b);
}

Main.java - Template Callback 패턴 추가

public class Main {

    public static void main(String[] args) {
        //1. 템플릿 메서드 패턴 사용
        Calculator calculator = new Plus();
        calculator.makeProcess("그냥더하기",1,2);
        calculator.executeProcess("그냥더하기");
        calculator.executeProcess("아무거나 입력하기");

        Calculator calculator1 = new DoublePlus();
        calculator.makeProcess("따블로 더하기1", 2, 2);
        calculator.executeProcess("따블로 더하기1");
        calculator1.makeProcess("따블로 더하기2", 4,4);
        calculator1.executeProcess("따블로 더하기2");

        //2. 템플릿 콜백 패턴 사용
        CalculatorForCallback calculatorForCallback = new CalculatorForCallback();
        calculatorForCallback.makeProcess("string", 1, 2, (a, b) -> a+b);
//        calculatorForCallback.makeProcess("string", 1, 2, new CallbackInterface() {
//            @Override
//            public int getResult(int a, int b) {
//                return a+b;
//            }
//        });
    }
}

장점

  • 템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다.
  • 템플릿 코드를 변경하지 않고 상속을 받아서 구체적인 알고리듬만 변경할 수 있다. 

단점

  • 리스코프 치환 원칙을 위반할 수도 있다.
  • 알고리듬 구조가 복잡할 수록 템플릿을 유지하기 어려워진다.

이렇게 템플릿 메서드 패턴과 템플릿 콜백 패턴에 대해 알아봤다. 

 

이 패턴의 의도와 이름을 생각해 보자면, 템플릿을 메서드 사용하듯이 우리가 공통적으로 사용하는 기능이 아닌가 생각된다. 

 

즉, 템플릿을 메서드처럼 사용할 수 있도록 기능하는 패턴이 아닌가 생각한다 (내 뇌피셜...) 

 

다만 템플릿을 가져다 쓰는건 좋은데 자기가 원하는 것만 맞추는 커스터 마이징이 가능한 템플릿이라고 이해가 된다. =

'디자인 패턴(구) > 행위 패턴' 카테고리의 다른 글

방문자(Visitor) 패턴이란?  (0) 2022.04.30
전략(Strategy) 패턴이란?  (0) 2022.04.27
상태(state) 패턴이란?  (0) 2022.04.26
감시자(Observer) 패턴이란?  (0) 2022.04.24
메멘토(Memento) 패턴이란?  (0) 2022.04.23