Template Method 패턴이란?
템플릿 기능을 가진 패턴으로 상위 클래스 쪽에 템플릿이 될 메서드가 정의되어 있고, 그 메서드 정의에 추상 메서드가 사용된다.
- 상위 클래스의 코드만 봐서는 최종적으로 어떻게 처리되는지 알 수 없다.
- 상위 클래스로 알 수 있는 것은 추상 메서드를 호출하는 방법 뿐이다.
템플릿이란 ?
문자 모양대로 구멍이 난 얆은 플라스틱 판으로 펜으로 그 구멍을 따라 그리면 손으로도 반듯하게 글씨를 쓸 수 있다.
- 어떤 형태의 문자인지는 템플릿을 보면 알 수 있지만 어떤 필기도구로 쓰냐에 따라서 색이 다를 수 있고 연필, 펜등 종류가 다를 수 있다.
추상 메서드를 실제로 구현하는 것은 하위 클래스이다.
- 하위 클래스에서 메서드를 구현하면 구체적인 처리 방식이 정해진다.
- 다른 하위 클래스에서 구현을 다르게 하면, 처리도 다르게 이루어 진다.
- 어느 하위 클래스에서 어떻게 구현하더라도 처리의 큰 흐름은 상위 클래스에서 구성한 대로 된다.
상위 클래스에서 처리의 뼈대를 결정하고 하위 클래스에서 그 구체적 내용을 결정하는 디자인 패턴을
Template Method 패턴이라고 부른다.
‘문자나 문자열을 5번 반복해서 표시’ 하는 프로그램
이름 | 설명 |
AbstractDisplay | 메서드 display만 구현된 추상 클래스 |
CharDisplay | 메서드 open, print, close를 구현하는 클래스 |
StringDisplay | 메서드 open, print, close를 구현하는 클래스 |
Main | 동작 테스트용 클래스 |
AbstractDisplay 클래스
display메서드가 정의되어 있고 이 메서드 안에서 open, print, close라는 세개의 메서드가 사용된다.
- 추상 메서드를 사용하는 display 메서드가 템플릿 메서드이다.
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(); //메서드 5회 출력
}
close(); //메서드 호출
}
}
display가 실제로 무슨일을 하는지는 open, print, close메서드를 구현하는 하위 클래스를 확인해 봐야 알 수 있다.
open, print, close 메서드를 실제로 구현하는 AbstractDisplay 클래스의 하위 클래스 2가지
CharDisplay 클래스
메서드 이름 | 처리 |
open | 문자열 “<<”를 표시 |
생성자에게 주어진 1문자를 표시 | |
close | 문자열 “>>”를 표시 |
public class CharDisplay extends AbstractDisplay{
private char ch;
public CharDisplay(char ch){ //생성자
this.ch = ch;
}
@Override
public void open() { //시작 문자열 "<<"
System.out.print("<<");
}
@Override
public void print() { //필드에 저장해 둔 문자를 1회 표시
System.out.print(ch);
}
@Override
public void close() { //종료 문자열 ">>
System.out.println(">>");
}
}
StringDisplay 클래스
메서드 이름 | 처리 |
open | 문자열 “+——+” 표시 |
생성자에게 주어진 문자열을 “|” 와 “|” 사이에 표시 | |
close | 문자열 “+——+” 표시 |
public class StringDisplay extends AbstractDisplay{
private String string; //표시해야 하는 문자열
private int width; // 문자열의 길이
public StringDisplay(String string){
this.string = string;
this.width = string.length();
}
@Override
public void open() {
printLine();
}
@Override
public void print() {
System.out.println("|"+string+"|");
}
@Override
public void close() {
printLine();
}
//open과 close에서 호출되어 "+---+" 문자열을 표시하는 메서드
private void printLine(){
System.out.print("+");
for (int i = 0; i < width; i++){
System.out.print("-");
}
System.out.println("+");
}
}
Main클래스 - 동작 테스트를 위한 클래스
public class templateMain {
public static void main(String[] args) {
//'H'를 가진 CharDisplay 인스턴스를 하나 만든다.
AbstractDisplay d1 = new CharDisplay('H');
//"Hello, world."를 가진 StringDisplay 인스턴스를 하나 만든다.
AbstractDisplay d2 = new StringDisplay("Hello, world.");
/**
* d1,d2 모두 같은 AbstractDisplay의 하위 클래스의 인스턴스이므로
* 상속한 display 메서드를 호출할 수 있다.
* 실제 동작은 추상 메서드를 구현한 CharDisplay,StringDisplay 클래스에서 정해진다.
*/
d1.display();
d2.display();
}
}
/* 실행결과
<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+
*/
Template Method 패턴의 등장인물
AbstractClass(추상 클래스) 역 - AbstractDisplay
템플릿 메서드를 구현하며, 그 템플릿 메서드에게 사용할 추상 메서드를 선언한다.
- 이 추상메서드는 하위 클래스인 ConcreateClass에서 구현된다.
ConcreateClass(구현 클래스) 역 - CharDisplay, StringDisplay
AbstractClass 역에서 정의된 추상 메서드를 구체적으로 구현한다.
- 여기서의 메서드는 AbstractClass의 템플릿 메서드에서 호출된다.
Template Method 패턴 클래스 다이어그램
로직을 공통화 할 수 있다.
Template Method를 사용하면 상위 클래스의 템플릿 메서드에 알고리즘이 기술되어 있으므로, 하위 클래스 쪽에는 알고리즘을 일일이 기술할 필요가 없어진다.
- Template Method 패턴을 사용하지 않고 복사 & 붙여 넣기 기능으로 ConcreateClass를 만들었을 때
- ConcreateClass1,ConcreateClass2 … 은 모두 비슷한 클래스 일 때 만든 직후는 괜찮겠지만, 나중에 ConcreateClass1 에서 버그가 발견되면 하나의 버그 수정 내용을 모든 ConcreateClass에 반영해야만 한다.
- Template Method 패턴을 적용해 프로그래밍하면 템플릿 메서드에 오류가 발생되더라도 템플릿 메서드만 수정하면 된다.
상위 클래스와 하위 클래스의 연계
Template Method 패턴에선 상위 클래스와 하위 클래스가 긴밀하게 연계하여 움직인다.
- 상위 클래스에서 선언된 추상 메서드를 실제로 하위 클래스에서 구현할 때는 그 메서드가 어떤 타이밍에 호출되는지 이해해야 한다.
- 상위 클래스의 소스 프로그램이 없으면, 하위 클래스 구현이 어려울 수 있다.
하위 클래스를 상위 클래스와 동일시한다.
예제에서 CharDisplay, StringDisplay의 인스턴스 모두 AbstractDisplay형 변수에 대입해서 display 메서드를 호출하고 있다.
상위 클래스형 변수가 있고 그 변수에 하위 클래스 인스턴스가 대입된다고 가정했을 때,
instanceof 등으로 하위 클래스의 종류를 특정하지 않아도 프로그램이 동작하게 만드는 것이 좋다.
- 상위 클래스형 변수에 하위 클래스의 인스턴스 중 어느 것을 대입해도 제대로 동작할 수 있게 하는 원칙을 “The Liskov Substitution Principle(LSP)” 이라고 한다.
추가적으로 생각할 것들 : 클래스 계층과 추상 클래스
상위 클래스에서 하위 클래스로 요청
클래스 계층에 관해 학습할 때 대부분 하위 클래스 관점에서 생각한다.
-상위 클래스에서 정의된 메서드를 하위 클래스에서 이용할 수 있다.
-하위 클래스에 약간의 메서드를 기술하는 것만으로 새로운 기능을 추가할 수 있다.
-하위 클래스에서 메서드를 오버라이드하면 동작을 변경할 수 있다.
상위 클래스의 입장이 되어 생각해보자.
- 상위 클래스에 추상 메서드가 선언되어 있으면 , 그 메서드는 당연히 ‘하위 클래스에서 구현’하게 된다.
추상 메서드 선언은 프로그램을 사용해 다음과 같이 주장하는 것이다.
-하위 클래스에서 그 메서드를 구현하기를 기대한다.
-하위 클래스에 해당 메서드 구현을 요청한다.
- 하위 클래스에는 상위 클래스에서 선언한 추상 메서드를 구현할 책임이 있다. 이것을 subclass responsibility(하위 클래스의 책임)라고 한다.
Java 프로그램에서 @Override 어노테이션은 해당 메서드가 상위 클래스나 인터페이스에서 선언된 메서드를
오버라이드했음을 나타낸다. @Override 어노테이션을 발견하면, 어떤 책임을 맡은 메서드인지 주의해서
읽어야 한다.
추상 클래스의 의의
추상 클래스는 인스턴스를 만들 수 없다.
- 추상 메서드에는 메서드의 본체가 기술되어 있지 않아, 구체적인 처리 내용은 알 수 없다.
- 메서드 이름을 정하고 그 메서드를 사용한 템플릿 메서드에 의해 처리를 기술할 수 있다.
- 실제 처리는 하위 클래스까지 가야 결정되지만, 추상 클래스 단계에서 처리 흐름을 형성하는 것이 중요하다.
상위 클래스와 하위 클래스의 협조
상위 클래스와 하위 클래스는 서로 협조하면서 프로그램을 구축한다.
- 상위 클래스에서 많이 기술하면 하위 클래스를 작성하기 편해지지만, 하위 클래스의 자유는 줄어든다.
- 상위 클래스에서 적게 기술하면 하위 클래스를 작성하기 힘들어지고, 또 각각의 하위 클래스에서 처리 기술이 중복될 수도 있다.
Template Method 패턴에서는 처리 내용의 뼈대는 상위 클래스에 기술하고 , 구체적인 내용은 하위 클래스에 기술한다.
어떤 수준에서 처리를 나눌지, 어떤 처리를 상위 클래스에 두고 어떤 처리를 하위 클래스에 둘지를
규정한 메뉴얼이 있는 것은 아니다. (프로그램을 설계하는 사람의 몫이다.)
관련패턴
Factory Method 패턴
Strategy 패턴
'책 > Java 언어로 배우는 디자인 패턴 입문' 카테고리의 다른 글
[디자인 패턴] Prototype패턴 (0) | 2023.03.12 |
---|---|
[디자인 패턴] Singleton패턴 (0) | 2023.03.11 |
[디자인 패턴] Factory Method패턴 (0) | 2023.03.11 |
[디자인 패턴] Adapter패턴 (0) | 2023.03.11 |
[디자인 패턴] Iterator패턴 (0) | 2023.02.10 |