Skip to content

템플릿 메소드 패턴 #

Find similar titles

5회 업데이트 됨.

Edit
  • 최초 작성자
    이크스
  • 최근 업데이트
    hyewonlee

Structured data

Category
Programming

Template(템플릿)이란 무엇인가 #

  • 템플릿이란 문자모양으로 구멍이 뚫려있는 얇은 플라스틱 판.
  • 그 구멍을 따라 펜으로 그리면 손으로도 반듯한 문자를 쓸 수 있다.
  • 템플릿의 구멍을 보면 어떤 모양의 문자인지는 알 수 있지만, 실제로 어떤 문자가 될지는 필기구에 의해 결정된다.
  • 펜을 사용하면 펜으로 쓴 문자가 되고, 연필을 사용하면 연필로 쓴 문자가 된다.
  • 그러나 어떤 필기구를 사용해도 쓰여진 문자는 템플릿 구멍의 형태와 동일하다.

Template Method Pattern이란? #

  • 탬플릿의 기능을 가진 패턴.
  • 상위 클래스 쪽에 탬플릿에 해당하는 메소드가 정의되어 있고, 그 메소드의 정의 안에는 추상 메소드가 사용되어 있다.
  • 따라서, 상위 클래스의 프로그램만 보면 추상 메소드를 어떻게 호출하고 있는지 알 수 있지만, 최종적으로 어떤 처리가 수행되는지 알 수 없다.
  • 추상 메소드를 실제로 구현하는 것은 하위 클래스이다. 하위 클래스 측에서 메소드를 구현하면 구체적인 처리가 결정된다.
  • 서로 다른 하위 클래스가 서로 다른 구현을 실행하면 서로 다른 처리가 실행될 것이다.
  • 그러나, 어떤 하위 클래스에서 어떤 구현을 하더라도 처리의 뼈대를 결정하고, 하위 클래스에서 그 구체적인 내용을 결정하는 디자인 패턴을 Template Method Pattern이라고 한다.

예제 #

  • AbstractDisplaysy - 메소드 display만 구현되고 있는 추상 클래스
  • CharDisplay - 메소드 Open, Print, Close 를 구현하고 있는 클래스
  • StringDisplay - 메소드 Open, Print, Close 를 구현하고 있는 클래스
  • Main - 동작 테스트용 클래스

1.AbstractDisplay 클래스 #

  • AbstractDisplay 클래스는 open, print, close, display 라는 메소드를 가지고 있다. 이 중에서 open, print, close 는 추상 메소드이고 display 메소드만이 구현되고 있다. AbstractDisplay 클래스에 쓰여있는 display 메소드의 정의를 읽어 보면 다음과 같은 처리를 실행하고 있다.

    open 메소드를 호출
    print 메소드를 5회 호출
    close 메소드를 호출
    

    그러면 open, print, close의 각 메소드는 무엇을 하고 있을까? AbstractDisplay 클래스를 읽어 보면, 이 세 가지 메소드는 추상 메소드이기 때문에 AbstractDisplay가 '실제로 무엇을 하고 있는지 AbstractDisplay 클래스만 보고서는 알 수 없다. 실제로 무엇을 하고 있는 가는 open,print, close를 구현하는 하위 클래스에게 맡기고 있다.

    Public abstract class AbstractDisplay {
         public abstract void open();
         public abstract void print();
         public abstract void close();  
         public final void display()  {
                open();
                for (int i = 0; i < 5; i++)  {
    
                      print();
                }
                 close();
     }
    

CharDisplay 클래스 #

  • 여기까지 이해가 되었다면 이번에는 하위 클래스의 하나인 CharDisplay 클래스를 살펴보면 상위 클래스인 AbstractDisplay 클래스에서 추상 메소드였던 open, print, close 가 모두 구현되어 있기 때문에 CharDisplay 클래스는 추상 클래스가 아니다. CharDisplay 클래스의 open, print, close 는 다음과 같은 처리를 실행하고 있다.

    open  : 문자열 "<<" 표시한다
    print : 생성자에서 주어진 1문자를 표시한다
    close : 문자열 ">>" 을 표시한다.
    

    이 상태에서 display 메소드를 호출된다면 어떻게 될까? 예를 들어 생성자에 'H' 라는 문자가 전달되었다고 가정하면

    <<HHHHH>>
    

    라는 문자열이 표시된다.

    public class CharDisplay extends AbstractDisplay  {
        private char ch;
        public CharDisplay (char ch)  {
                 this.ch = ch;
       }
       public void open()  {
            System.out.print("<<");
       }
       public void print()   {
              System.out.print(ch);
        }
        public void close()  {
           System.out.print(">>");
        }
    }
    

StringDisplay 클래스 #

  • 그러면 또 하나의 하위 클래스인 StringDisplay 클래스를 살펴 보면 물론 여기에서도 open, print, close 가 구현되고 있다.
    이번에는 어떤 처리를 수행하고 있을까?
    StringDisplay의 open, print, close는 다음과 같은 처리를 실행하고 있다. 이 상태에서 display 메소드가 호출된다면 어떻게 될까? 생성자에 "Hello, world" 라는 문자열이 전달되어 있다고 가정하면

    ----------------
    ㅣHello, world.ㅣ
    ㅣHello, world.ㅣ
    ㅣHello, world.ㅣ
    ㅣHello, world.ㅣ
    ----------------
    

    와 같이 테두리에 둘러싸인 문자열이 표시된다.

    open  : 문자열 " +--------+"을 표시한다
    print : 생성자에서 주어진 문자열을 "ㅣ" 와 "ㅣ" 사이에 표시한다.
    close : 문자열 " +--------+을 표시한다.
    

    아래는 예제이다.

    public class StringDisplay extends AbstractDisplay {
         private String string;
         private int width;
         pubilc StringDisplay (String string)  {
                this.string  = string;
                this.width   = string.getBytes().length;
         }
         public void open()  {
              printLine();
         }
         public void print()   {
               System.out.println("l" + string + "l");
         }
         public void close()  {
              printLine();
        }
        private void printLine()  {
                 System.out.print("+");
                 for(int i= 0; i< width; i++) {
                      System.out.print("-");
                 }
                System.out.println("+");        
       }
    }
    

Main 클래스 #

  • Main 클래스는 동작 테스트를 실행한다. 지금까지 만들었던 CharDisplay 클래스와 StringDisplasy 클래스의 인스턴스를 만들어 display 메소드를 호출하고 있다.

    public class Main  {
      public static void main(String[] args)   {
           AbstractDisplay d1 = new CharDisplay('H');
           AbstractDisplay d2 = new StringDisplay("hello, world.");
           AbstractDisplay d3 = new StringDisplay("안녕하세요.");
          d1.display();
          d2.display();
          d3.display();
      }
    }
    

    Template Method 패턴의 등장인물 #

    1. AbstractClass(추상 클래스)의 역할 AbstractClass 는 템플릿 메소드를 구현한다. 또한 그 템플릿 메소드에서 사용하고 있는 추상 메소드를 선언한다. 이 추상 메소드는 하위 클래스인 ConcreteClass 역할에 의해 구현되며, 예제 프로그램에서는 AbstractDisplay 클래스가 이 역할을 한다.
    2. ConcreteClass(구현 클래스)의 역할 AbstractClass 역할에서 정의되어 있는 추상 메소드를 구체적으로 구현하며, 여기에서 구현한 메소드는 AbstractClass 역의 템플릿 메소드에서 호출된다. 예제 프로그램에서는 CharDisplay 클래스나 StringDisplay 클래스가 이 역할을 하고있다.

Template Method 세부내용 #

  • 로직을 공통화할 수 있다. Template Method 패턴을 사용하면 어떤 이점이 있을까? 상위 클래스의 템플릿 메소드에서 알고리즘이 기술되어 있으므로, 하위 클래스측에서는 알고리즘을 일일이 기술할 필요가 없다. 예를 들어 Template Method 패턴을 사용하지 않고, 에디터의 복사 & 붙여넣기 기능을 사용해서 복수의ConcreteClass 역할을 만들었다고 가정한다면 ConcreteClass1, ConcreteClass2, ConcreteClass3 은 모두 비슷하지만 다른 클래스가 된다. 작성한 직 후에는 괜찮아지나 나중에 ConcreteClass1에 버그가 발견되면 1개의 버그를 수정하기 위해 모든 ConcreteClass를 수정해야 된다. 그런 점에서 Template Method 패턴으로 프로그래밍을 하면 템플릿 메소드에 오류가 발견되더라도 템플릿 메소드만 수정하면 된다.

  • 상위 클래스와 하위 클래스의 연계. Template Method 패턴에서는 상위 클래스와 하위 클래스가 긴밀하게 연락을 취하며 작동하고 있다. 따라서 상위 클래스에서 선언된 추상 메소드를 실제로 하위 클래스에서 구현할 때에는 그 메소드가 어느 타이밍에 호출되는지 이해해야 한다. 상위 클래스와 소스 프로그램이 없으면 하위 클래스의 구현이 어려울 수도 있다.

  • 하위 클래스를 상위 클래스와 동일 시 한다. 예제 프로그램 안에서는 CharDisplay의 인스턴스도, StringDisplay 의 인스턴스도 AbstractDisplay형의 변수에 대입하고 있다. 그리고 display 메소드를 호출하고 있다. 상위 클래스형의 변수가 있고, 그 변수에 하위 클래스형의 인스턴스가 대입된다고 가정하면, 이 때 instanceof 등으로 하위 클래스의 종류를 특정하지 않아도 프로그램이 작동하도록 만드는 것이 좋다. "상위 클래스형의 변수에 하위 클래스의 어떤한 인스턴스를 대입해도 제대로 작동할 수 있도록 한다" 는 원칙은 LSP이라고 불린다., 이 LSP는 Template Method 패턴에 국한되지 않는 상속의 일반적인 원칙이다.

0.0.1_20231010_1_v71