나를 기록하다
article thumbnail
반응형

JAVA 언어로 배우는 디자인 패턴 입문

Template Method 패턴

템플릿이란 무엇인가?

구멍이 난 얇은 플라스틱 판과 같다. 어떤 형태의 문자인지는 알 수 있지만 실제로 어떤 문자가 될지는 구체적인 필기 도구가 정해지기 전까진 모른다. 사인펜, 연필, 색연필 등 어떤 필기구를 사용하더라도 쓰여진 문자는 템플릿의 구멍 모양과 같다.

Template Method 패턴이란 무엇인가?

템플릿 기능을 가진 패턴으로, 상위 클래스 쪽에 템플릿이 될 메서드가 정의되어 있고, 그 메서드 정의에 추상 메서드가 사용된다. 따라서 상위 클래스의 코드만 봐서는 최종적으로 어떻게 처리되는지 알 수 없다. 상위 클래스로 알 수 있는 것은 추상 메서드를 호출하는 방법 뿐이다.

 

추상 메서드를 실제로 구현하는 것은 하위 클래스이다. 하위 클래스에서 메서드를 구현하면 구체적인 처리 방식이 정해진다. 다른 하위 클래스에서 구현을 다르게 하면, 처리도 다르게 이루어진다. 그러나, 어느 하위 클래스에서 어떻게 구현하더라도 처리의 큰 흐름은 상위 클래스에서 구성한 대로 된다.

Template Method 패턴
상위 클래스에서 처리의 뼈대를 결정하고 하위 클래스에서 그 구체적 내용을 결정하는 디자인 패턴

예제 프로그램

문자나 문자열을 5번 반복해서 표시하는 간단한 프로그램

이름 설명
AbstractDisplay 메서드 display만 구현된 추상 클래스
CharDisplay 메서드 open, print, close를 구현하는 클래스
StringDisplay 메서드 open, print, close를 구현하는 클래스
Main 동작 테스트용 클래스

예제 프로그램의 클래스 다이어그램


AbstractDisplay 클래스

메서드 정의
- open 메서드를 호출한다
- print 메서드를 5회 호출한다
- close 메서드를 호출한다

실제로 무슨 일을 하는지는 open, print, close를 구현하는 하위 클래스를 확인해 봐야 알 수 있다.

public abstract class AbstractDisplay {
    //open, print, close는 하위 클래스에 구현을 맡기는 추상 메서드
    public abstract void open();
    public abstract void print();
    public abstract void close();

    //display는 AbstractDisplay에서 구현하는 메서드
    public final void display() {
        open();
        for (int i = 0; i < 5; i++) {
            print();
        }
        close();
    }
}

 


CharDisplay 클래스

메서드 처리

메서드 이름 처리
open 문자열 "<<"를 표시
print 생성자에서 주어진 1문자를 표시
close 문자열 ">>"를 표시

 

예를 들어 display 메서드가 호출되고 생성자에 'P'라는 문자가 전달되면, 다음과 같은 문자열이 표시된다.

<<PPPPP>>

 

public class CharDisplay extends AbstractDisplay {
    private char ch; //표시해야 하는 문자
    
    public CharDisplay(final char ch) {
        this.ch = ch;
    }

    @Override
    public void open() {
        System.out.print("<<");
    }

    @Override
    public void print() {
        System.out.print(ch);
    }

    @Override
    public void close() {
        System.out.println(">>");
    }
}

StringDisplay 클래스

메서드 처리

메서드 이름 처리
open 문자열 "+----+"을 표시
print 생성자에서 주어진 문자열을 "|"와 "|" 사이에 표시
close 문자열 "+----+"을 표시

예를 들어 display 메서드가 호출되고 생성자에 "Hello, World!"라는 문자열이 전달되면, 다음과 같이 테두리로 둘러싸인 문자열이 표시된다.

+---------------+
| Hello, World! |
| Hello, World! |
| Hello, World! |
| Hello, World! |
| Hello, World! |
+---------------+
public class StringDisplay extends AbstractDisplay {
    private String string; //표시해야 하는 문자열
    private int width; //문자열의 길이

    public StringDisplay(final String string) {
        this.string = string;
        this.width = string.length() + 2;
    }

    @Override
    public void open() {
        printLine();
    }


    @Override
    public void print() {
        System.out.println("| " + string + " |");
    }

    @Override
    public void close() {
        printLine();
    }

    private void printLine() {
        System.out.print("+");
        for (int i = 0; i < width; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }
}

Main 클래스

public class Main {
    public static void main(String[] args) {
        AbstractDisplay d1 = new CharDisplay('H');
        AbstractDisplay d2 = new StringDisplay("Hello, World!");

        d1.display();
        d2.display();
    }
}

실행 결과

<<HHHHH>>
+---------------+
| Hello, World! |
| Hello, World! |
| Hello, World! |
| Hello, World! |
| Hello, World! |
+---------------+

 

d1에 의한 표시(CharDisplay)와 d2에 의한 표시(StringDisplay)가 순차적으로 출력되는 것을 확인할 수 있다.


Template Method의 클래스 다이어그램

Template Method 패턴의 클래스 다이어그램

AbstractClass(추상 클래스)

  • 템플릿 메서드를 구현한다
  • 템플릿 메서드에서 사용할 추상 메서드를 선언한다
  • 추상 메서드는 하위 클래스인 ConcreteClass에서 구현된다

ConcreteClass(구현 클래스)

  • 추상 클래스에서 정의된 추상 메서드를 구체적으로 구현한다
  • 여기서 구현하는 메서드는 추상 클래스의 템플릿 메서드에서 호출된다

Template Method의 특징

로직을 공통화할 수 있다

상위 클래스의 템플릿 메서드에 알고리즘이 기술되어 있으므로, 하위 클래스 쪽에는 알고리즘을 일일이 기술할 필요가 없어진다.

[예시] Template Method 패턴을 사용하지 않는다면?
Template Method 패턴을 사용하지 않고 편집기의 복사 & 붙여넣기 기능으로 ConcreteClass를 여러 개 만들었다고 가정하자.
ConcreteClass1, ConcreteClass2, ConcreteClass3 ...은 모두 비슷비슷한 클래스다. 만든 직후는 괜찮겠지만, 나중에 ConcreteClass1에서 버그가 발견된다면 어떻게 해야할까? 하나의 버그 수정 내용을 모든 ConcreteClass에 반영해야 한다.

하지만, Template Method 패턴을 적용해 프로그래밍하면 템플릿 메서드에 오류가 발견되더라도 템플릿 메서드만 수정하면 된다.

상위 클래스와 하위 클래스의 연계 플레이

Template Method 패턴에선 상위 클래스와 하위 클래스가 긴밀하게 연계하여 움직인다. 그러므로 상위 클래스에서 선언된 추상 메서드를 실제로 하위 클래스에서 구현할 때는 그 메서드가 어떤 타이밍에 호출되는지 이해해야만 한다. 상위 클래스의 소스 프로그램이 없으면, 하위 클래스 구현이 어려울 수도 있다.


하위 클래스를 상위 클래스와 동일시한다

예제 프로그램에서는 CharDisplay의 인스턴스와 StringDisplay의 인스턴스 모두 AbstractDisplay형 변수에 대입해서 display 메서드를 호출한다.

 

상위 클래스형 변수가 있고 그 변수에 하위 클래스 인스턴스가 대입된다고 가정했을 때, instanceof 등으로 하위 클래스의 종류를 특정하지 않아도 프로그램이 동작하게 만드는 것이 좋다.

 

상위 클래스형 변수에 하위 클래스의 인스턴스 중 어느 것을 대입해도 제대로 동작할 수 있게 하느 원칙을 SOLID 원칙의 L에 해당하는 LSP(Liscov Substitution Principle; 리스코프 치환 원칙)이라 한다.


관련 패턴

Factory Method 패턴

  • Template Method 패턴을 인스턴스 생성에 응용한 전형적인 예가 Factory Method  패턴이다.

Strategy 패턴

  • Template Method 패턴에서는 상속을 이용하여 프로그램 동작을 변경할 수 있다. 상위 클래스에서 프로그램 동작의 큰 틀을 결정하고 하위 클래스에서 구체적인 해동을 규정하기 때문이다.
  • 반면에 Strategy 패턴에서는 위임을 이용하여 프로그램의 동작을 변경할 수 있다. Strategy 패턴에서는 프로그램 일부를 변경하기보다는 알고리즘 전체를 모두 전환한다.

더 알아두면 좋은 내용: 클래스 계층과 추상 클래스

상위 클래스에서 하위 클래스로 요청

보통 클래스 계층에 관해 학습할 때 대부분 하위 클래스 관점에서 생각한다. 즉, 다음과 같은 점에 추측하는 경향이 있다.

하위 클래스 관점에서의 추측
- 상위 클래스에서 정의된 메서드를 하위 클래스에서 이용할 수 있다.
- 하위 클래스에 약간의 메서드를 기술하는 것만으로 새로운 기능을 추가할 수 있다.
- 하위 클래스에서 메서드를 오버라이드하면 동작을 변경할 수 있다.

 

여기서 관점을 바꿔서 상위 클래스의 입장이 되어 생각해보자. 상위 클래스에 추상 메서드가 선언되어 있으면, 그 메서드는 당연히 하위 클래스에서 구현하게 된다. 바꿔 말해, 추상 메서드 선언은 프로그램을 사용해 다음과 같이 주장하는 것이다.

추상 메서드 선언의 의미
- 하위 클래스에서 그 메서드를 구현하기를 기대한다.
- 하위 클래스에서 해당 메서드 구현을 요청한다.

 

하위 클래스에는 상위 클래스에서 선언한 추상 메서드를 구현할 책임이 있다. 이것을 subclass responsibility(하위 클래스의 책임)이라 한다.


추상 클래스의 의의

추상 클래스는 인스턴스를 만들 수 없다. 추상 메서드에는 메서드의 본체가 기술되어 있지 않아 구체적인 처리 내용은 알 수 없다. 하지만 메서드 이름을 정하고 그 메서드를 사용한 템플릿 메서드에 의해 처리를 기술할 수 있다. 실제 처리 내용은 하위 클래스까지 가야 결정되지만, 추상 클래스 단계에서 처리 흐름을 형성하는 것은 중요하다.


상위 클래스와 하위 클래스의 협조

상위 클래스와 하위 클래스는 서로 협조하면서 프로그램을 구축한다. 상위 클래스에서 많이 기술하면 하위 클래스를 작성하기 편해지지만, 하위 클래스의 자유는 줄어든다. 반대로 상위 클래스에서 적게 기술하면 하위 클래스를 작성하기 힘들어지고, 또 각각의 하위 클래스에서 처리 기술이 중복될 수도 있다.

 

Template Method 패턴에서는 처리 내용의 뼈대는 상위 클래스에 기술하고, 구체적인 내용은 하위 클래스에 기술한다. 어떤 수준에서 처리를 나눌지, 어떤 처리를 상위 클래스에 두고 어떤 처리를 하위 클래스에 둘지를 규정한 매뉴얼이 있는 것은 아니다. 그것은 프로그램을 설계하는 사람의 몫이다.

반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...