Design Pattern
2009. 5. 6. 12:27
템플릿의 기능을 가진 패턴.
상위클래스에 템플릿에 해당하는 메소드가 정의, 추상메소드가 사용.
상위 클래스의 프로그램만 보면 추상 메소드를 어떻게 호출하고 있는 알 수 있지만, 최종적으로 어떤 처리가 수행되는지는 알 수 없다.
추상메소드를 실제로 구현하는 것은 하위 클래스다. 서로 다른 하위 클래스가 서로 다른 구현을 하더라도 처리의 큰 흐름은 상위 클래스에서 결정한대로 이루어진다.
(파워포인트2007로 작성...연결선이 맘에들지 않는다.;)
클래스 일람
AbstractDisplay : 추상클래스로서 메소드 display만 구현되고 있다.
CharDisplay : 메소드 open,print,close를 구현
StringDisplay : 위와 같지만 printLine도 추가
Main : 동작 테스트용
//AbstractDisplay
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 메소드에서 구현되고 있는 내용.
}
}
//CharDisplay
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(“>>”); // 종료 문자열 “>>”을 표시.
}
}
//StringDisplay
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
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에서 결정한다.
}
}
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 메소드에서 구현되고 있는 내용.
}
}
//CharDisplay
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(“>>”); // 종료 문자열 “>>”을 표시.
}
}
//StringDisplay
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
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에서 결정한다.
}
}
이 패턴을 이용하면 상위클래스의 템플릿메소드에서 알고리즘이 기술되어 있으므로, 하위 클래스측에서는 알고리즘을 일일이 기술할 필요가 없다.
상위클래스에서 하위 클래스에게 요청
-상위클래스에서 정의되어 있는 메소드를 하위클래스에서 이용할 수 있다
-하위클래스에 약간의 메소드를 기술해서 새로운 기능을 추가할 수 있다
-하위클래스에서 메소드를 오버라이드하면 동작을 변경할 수 있다.
추상클래스의 의의
추상메소드는 본체가 기술되어 있지 않아서 구체적인 처리내용을 알 수 없으나, 메소드의 이름을 결정하고 메소드를 사용한 템플릿메소드에 의해 처리를 기술하는 것은 가능하다.
실제의 처리 내용은 하위 클래스에서 결정하지만, 추상클래스의 단계에서 처리의 흐름을 형성하는 것은 중요하다.