[디자인패턴] template method 패턴(탬플릿 메소드 패턴) 예제 :: 개발/일상_Mr.lee

[디자인패턴] template method 패턴(탬플릿 메소드 패턴) 예제

Posted by Mr.mandu.
2021. 1. 11. 21:32 개발/디자인패턴

안녕하세요.

디자인 패턴 세번째 시간 입니다.


다른 책들도 보면서 공부를 해봤지만

이번 책을 통해 개념위주의 간단한 예제를 통해 알아보니

예전보다는 약간(?)의 이해도가 더 높은 것 같습니다.


이번에는 Template Method 패턴에 대해 알아 보겠습니다.


Template Method 란?

상위 클래스에서 뼈대를 결정하고, 하위 클래스에서 그 구체적인 내용을 결정


상위 클래스쪽에 탬플릿에 해당하는 메소드가 정의 되어 있고,

그 메소드의 정의 안에는 추상 메소드로 정의


즉, 상위 클래스를 보면 추상 메소드를 어떻게 호출하는지 알 수 있지만, 

최종적으로는 어떤 처리가 수행되는지는 알 수 없음.


이름 

 설명 

 AbstractDisplay 

 메소드 display만 구현되고 있는 추상 클래스 

 CharDisplay

  AbstractiDisplay 클래스 구현

 StringDisplay 

  AbstractiDisplay 클래스 구현

 Main

 동작 테스트 


AbstractDisplay 클래스

package TemplateMethod;

public abstract class AbstractDisplay {  // 추상 클래스 AbstractDisplay
    public abstract void open();         // 하위 클래스에 구현을 맡기는 추상 메소드 (1) open
    public abstract void print();        // 하위 클래스에 구현을 맡기는 추상 메소드 (2) print
    public abstract void close();        // 하위 클래스에 구현을 맡기는 추상 메소드 (3) close
    public final void display() {        // 추상 클래스에서 구현되고 있는 메소드 display
        open();                      	 // 우선 open하고…
        for (int i = 0; i < 5; i++) {    // 5번 print을 반복하고…
            print();                    
        }
        close();     // … 마지막으로 close한다. 이것이 display 메소드에서 구현되고 있는 내용.
    }
}


소스 코드를 보면 동작에 대한 메소드는 추상 메소드로 선언하고 있으며,

동작 부분 display() 함수의 동작을 구현하고 있습니다.



CharDisplay 클래스

package TemplateMethod;

public class CharDisplay extends AbstractDisplay {  // CharDisplay는 AbstractDisplay의 
// 하위 클래스.
    private char ch;                              // 표시해야 할 문자
    public CharDisplay(char ch) {                  // 생성자에서 전달된 문자 ch을
        this.ch = ch;                             // 필드에 기억해 둔다.
    }
    public void open() {                            // 상위 클래스에서는 추상 메소드였다.
						  // 여기에서 오버라이드해서 구현.
        System.out.print("<<");                     // 개시 문자열"<<"을 표시한다.
    }
    public void print() {                           // print 메소드도 여기에서 구현한다.
// 이것이 display에서 반복해서 호출된다.
        System.out.print(ch);                      // 필드에 기억해 둔 문자를 1개 표시한다.
    }
    public void close() {                           // close 메소드도 여기에서 구현.
        System.out.println(">>");                   // 종료 문자열 ">>"을 표시.
    }
}


추상 클래스 AbstractDisplay를 상속 받아 추상 메소드를 구현하고 있습니다.

마찬가지로 StringDisplay 클래스도 추상 메소드를 구현하고 있습니다.


Stringdisplay 클래스

package TemplateMethod;

public class StringDisplay extends AbstractDisplay {    // StringDisplay도 
// AbstrctDisplay의 하위 클래스.
    private String string;                            // 표시해야 할 문자열.
    private int width;                              // 바이트 단위로 계산한 문자열의 「폭」.
    public StringDisplay(String string) {               // 생성자에서 전달된 문자열 string을
        this.string = string;                           // 필드에 기억.
        this.width = string.getBytes().length;          // 그리고 바이트 단위의 폭도 필드에
							// 기억해 두고 나중에 사용한다.
    }
    public void open() {                             // 오버라이드해서 정의한 open 메소드.
        printLine();                                 // 이 클래스의 메소드 printLine에서
                                                     // 선을 그리고 있다.
    }
    public void print() {                               // print 메소드는
        System.out.println("|" + string + "|");     // 필드에 기억해 둔 문자열의
   							// 전후에 "|"을 붙여서 표시.
    }
    public void close() {                               // close 메소드는
        printLine();                                    // open 처럼 printLine 메소드에서
                                                       // 선을 그리고 있다.
    }
    private void printLine() {                  // open과 close에서 호출된 printLine 메소드이다.
 						// private이기 때문에 이 클래스 안에서만 사용된다.
        System.out.print("+");                // 테두리의 모서리를 표현하는"+" 마크를 표시.
        for (int i = 0; i < width; i++) {       // width개의 "-"을 표시하고
            System.out.print("-");            // 테두리 선으로 이용한다.
        }
        System.out.println("+");              // 테두리의 모서리를 표현하는 "+" 마크를 표시.
    }
}


실제 소스를 테스트하기위한 Main 클래스 입니다.



Main 클래스

package TemplateMethod;

public class Main {
    public static void main(String[] args) {
	// 'H'을 가진 CharDisplay 인스턴스를 1개 만든다>
        AbstractDisplay d1 = new CharDisplay('H');
	// "Hello, world."을 가진 StringDisplay의 인스턴스를 1개 만든다.
        AbstractDisplay d2 = new StringDisplay("Hello, world.");
    	// "안녕하세요."를 가진 StringDisplay의 인스턴스를 1개 만든다.
        AbstractDisplay d3 = new StringDisplay("안녕하세요.");
        d1.display();   // d1, d2, d3 모두 AbstractDisplay의 하위클래스의 인스턴스이기 때문에
        d2.display();   // 상속한 display메소드를 호출할 수 있다.
        d3.display();   // 실제 동작은 CharDisplay나 StringDisplay에서 결정한다.
    }
}


포스팅을 하다가 문득 생각이 들었습니다.

'클래스 안의 추상 메소드로 선언하는 것과

인터페이스를 선언해서 처리하는 것의 차이는 무엇일까?'


정확한 답은 찾지 못하였지만, 

인터페이스의 모든 함수는 추상 메소드이기때문에 이를 활용하여 동작을 구현 할수 없습니다.

위의 소스와 비교하면 AbstractDisplay 클래스의 public final void display() 함수와 같이 말이죠.


또한,

'상위 클래스형의 변수에 하위 클래스의 어떠한 인스턴스를 대입해도 제대로 작동할 수 있도록 한다"라는 

원칙 LSP에도 부합합니다.


아직은 부족하지만 여러 경험을 통해 

좀더 양질의 포스팅을 할 수 있도록 하겠습니다.


고생하세요.


※ 본 포스팅은 Java언어로 배우는 디자인 패턴 입문 책을 공부하며 작성하고 있습니다.