- Decorator 패턴 : 장식들과 내용물을 동일시한다
Decorator 패턴은 중심이 되는 객체가 있고, 거기에 장식이 되는 기능을 하나씩 추가해서 목적에 더 맞는 객체로 만들어 가는 패턴이다. 객체에 장식을 더해 가는 디자인 패턴이라 이해하면 된다.
- 어떤 경우에 사용할까?
Decorator 패턴에서는 장식틀과 내용물이 공통의 인터페이스를 가지지만, 감싸면 감쌀수록 기능이 추가된다. 이때 감싸지는 쪽을 수정할 필요는 없기에, 내용물을 변경하지 않고 기능을 추가할 수 있다.
- 예제 코드
이름 | 설명 |
Display | 문자열 표시용 추상 클래스 |
StringDisplay | 1행으로 구성된 문자열 표시용 클래스 |
Border | '장식틀'을 나타내는 추상 클래스 |
SideBorder | 좌우에만 장식틀을 붙이는 클래스 |
FullBorder | 상하좌우에 장식틀을 붙이는 클래스 |
Main | 동작 테스트용 클래스 |
- Display 클래스
public abstract class Display {
public abstract int getColumns(); // 가로 문자 수를 얻는다
public abstract int getRows(); // 세로 행 수를 얻는다
public abstract String getRowText(int row); // row행째 문자열을 얻는다
// 모든 행을 표시한다
public void show() {
for(int i = 0 ; i < getRows() ; i++) {
System.out.println(getRowText(i));
}
}
}
- StringDisplay 클래스
public class StringDisplay extends Display {
private String string; // 표시 문자열
public StringDisplay(String string) {
this.string = string;
}
@Override
public int getColumns() {
return string.length();
}
@Override
public int getRows() {
return 1; // 행수는 1
}
@Override
public String getRowText(int row) {
if(row != 0) {
throw new IndexOutOfBoundsException();
}
return string;
}
}
- Border 클래스
public abstract class Border extends Display {
protected Display display; // 이 장식들이 감싸는 '내용물'
protected Border(Display display) { // 인스턴스 생성 시 '내용물'을 인수로 지정
this.display = display;
}
}
- SideBorder 클래스
public class SideBorder extends Border {
private char borderChar; // 장식 문자
// 내용물이 될 Display와 장식 문자를 지정
public SideBorder(Display display, char ch) {
super(display);
this.borderChar = ch;
}
@Override
public int getColumns() {
// 문자 수는 내용물의 양쪽에 장식 문자만큼 더한 것
return 1 + display.getColumns() + 1;
}
@Override
public int getRows() {
// 행수는 내용물의 행수와 같다
return display.getRows();
}
@Override
public String getRowText(int row) {
// 지정 행의 내용은 내용물의 지정 행 양쪽에 장식 문자를 붙인 것
return borderChar + display.getRowText(row) + borderChar;
}
}
FullBorder 클래스
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
@Override
public int getColumns() {
// 문자 수는 내용물 양쪽에 좌우 장식 문자만큼 더한 것
return 1 + display.getColumns() + 1;
}
@Override
public int getRows() {
// 행수는 내용물의 행수에 상하 장식 문자만큼 더한 것
return 1 + display.getRows() + 1;
}
@Override
public String getRowText(int row) {
// 상단 테두리
if(row == 0) {
return "+" + makeLine('-', display.getColumns()) + "+";
}
// 하단 테두리
else if(row == display.getRows() + 1) {
return "+" + makeLine('-', display.getColumns()) + "+";
}
else {
return "|" + display.getRowText(row - 1) + "|";
}
}
// 문자 ch로 count 수만큼 연속한 문자열을 만든다
private String makeLine(char ch, int count) {
StringBuilder line = new StringBuilder();
for(int i = 0 ; i < count ; i++) {
line.append(ch);
}
return line.toString();
}
}
- Main 클래스
public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("Hello, world.");
Display b2 = new SideBorder(b1, '#');
Display b3 = new FullBorder(b2);
b1.show();
b2.show();
b3.show();
Display b4 =
new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("Hello, world.")
),
'*'
)
)
),
'/'
);
b4.show();
}
}
참고문헌 : 유키 히로시 저/김성훈 역(2022), JAVA 언어로 배우는 디자인 패턴 입문, 영진닷컴
참고자료 : https://refactoring.guru/ko/design-patterns/decorator