![데코레이터](/images/patterns/cards/decorator-mini.png?id=d30458908e315af195cb183bc52dbef9)
자바로 작성된 데코레이터
데코레이터는 구조 패턴이며 새로운 행동들을 특수 래퍼 객체들 내에 넣어서 이러한 행동들을 객체들에 동적으로 추가할 수 있도록 합니다.
데코레이터를 사용하여 객체들을 제한 없이 래핑할 수 있습니다. 왜냐하면 대상 객체들과 데코레이터들은 같은 인터페이스를 따르기 때문입니다. 결과 객체는 모든 래퍼의 스태킹된 행동을 가질 것입니다.
복잡도:
인기도:
사용 예시들: 데코레이터는 자바 코드, 특히 스트림과 관련된 코드에서 꽤 표준적입니다.
다음은 코어 자바 라이브러리로부터 가져온 데코레이터의 몇 가지 예시들입니다:
-
java.io.InputStream
,OutputStream
,Reader
와Writer
의 모든 자식 클래스들은 같은 유형의 객체들을 수락하는 생성자들이 있습니다. -
java.util.Collections
와 그의 메서드들:checkedXXX()
,synchronizedXXX()
,unmodifiableXXX()
. -
javax.servlet.http.HttpServletRequestWrapper
와HttpServletResponseWrapper
식별: 데코레이터는 같은 클래스의 객체 또는 인터페이스를 현재 클래스로 수락하는 생성 메서드들 또는 생성자들로 인식할 수 있습니다.
인코딩 및 압축 데코레이터들
이 예시는 코드를 변경하지 않고 객체의 행동을 조정하는 방법을 보여줍니다.
처음에 비즈니스 로직 클래스는 일반 텍스트로만 데이터를 읽고 쓸 수 있었으나 그다음 표준 작업을 래핑 된 객체 내에 실행 후 새로운 행동을 추가하는 몇 개의 작은 래퍼 클래스들을 만들었습니다.
첫 번째 래퍼는 데이터를 암호화 및 해독하고 두 번째 래퍼는 데이터를 압축 및 추출합니다.
또 한 데코레이터를 다른 데코레이터로 래핑하여 이러한 래퍼들을 결합할 수도 있습니다.
decorators
decorators/DataSource.java: 읽기 및 쓰기 작업을 정의하는 공통 데이터 인터페이스
package refactoring_guru.decorator.example.decorators;
public interface DataSource {
void writeData(String data);
String readData();
}
decorators/FileDataSource.java: 간단한 데이터 판독기-작성기
package refactoring_guru.decorator.example.decorators;
import java.io.*;
public class FileDataSource implements DataSource {
private String name;
public FileDataSource(String name) {
this.name = name;
}
@Override
public void writeData(String data) {
File file = new File(name);
try (OutputStream fos = new FileOutputStream(file)) {
fos.write(data.getBytes(), 0, data.length());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
@Override
public String readData() {
char[] buffer = null;
File file = new File(name);
try (FileReader reader = new FileReader(file)) {
buffer = new char[(int) file.length()];
reader.read(buffer);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
return new String(buffer);
}
}
decorators/DataSourceDecorator.java: 추상 기초 데코레이터
package refactoring_guru.decorator.example.decorators;
public abstract class DataSourceDecorator implements DataSource {
private DataSource wrappee;
DataSourceDecorator(DataSource source) {
this.wrappee = source;
}
@Override
public void writeData(String data) {
wrappee.writeData(data);
}
@Override
public String readData() {
return wrappee.readData();
}
}
decorators/EncryptionDecorator.java: 암호화 데코레이터
package refactoring_guru.decorator.example.decorators;
import java.util.Base64;
public class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) {
super(source);
}
@Override
public void writeData(String data) {
super.writeData(encode(data));
}
@Override
public String readData() {
return decode(super.readData());
}
private String encode(String data) {
byte[] result = data.getBytes();
for (int i = 0; i < result.length; i++) {
result[i] += (byte) 1;
}
return Base64.getEncoder().encodeToString(result);
}
private String decode(String data) {
byte[] result = Base64.getDecoder().decode(data);
for (int i = 0; i < result.length; i++) {
result[i] -= (byte) 1;
}
return new String(result);
}
}
decorators/CompressionDecorator.java: 압축 데코레이터
package refactoring_guru.decorator.example.decorators;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
public class CompressionDecorator extends DataSourceDecorator {
private int compLevel = 6;
public CompressionDecorator(DataSource source) {
super(source);
}
public int getCompressionLevel() {
return compLevel;
}
public void setCompressionLevel(int value) {
compLevel = value;
}
@Override
public void writeData(String data) {
super.writeData(compress(data));
}
@Override
public String readData() {
return decompress(super.readData());
}
private String compress(String stringData) {
byte[] data = stringData.getBytes();
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));
dos.write(data);
dos.close();
bout.close();
return Base64.getEncoder().encodeToString(bout.toByteArray());
} catch (IOException ex) {
return null;
}
}
private String decompress(String stringData) {
byte[] data = Base64.getDecoder().decode(stringData);
try {
InputStream in = new ByteArrayInputStream(data);
InflaterInputStream iin = new InflaterInputStream(in);
ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
int b;
while ((b = iin.read()) != -1) {
bout.write(b);
}
in.close();
iin.close();
bout.close();
return new String(bout.toByteArray());
} catch (IOException ex) {
return null;
}
}
}
Demo.java: 클라이언트 코드
package refactoring_guru.decorator.example;
import refactoring_guru.decorator.example.decorators.*;
public class Demo {
public static void main(String[] args) {
String salaryRecords = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000";
DataSourceDecorator encoded = new CompressionDecorator(
new EncryptionDecorator(
new FileDataSource("out/OutputDemo.txt")));
encoded.writeData(salaryRecords);
DataSource plain = new FileDataSource("out/OutputDemo.txt");
System.out.println("- Input ----------------");
System.out.println(salaryRecords);
System.out.println("- Encoded --------------");
System.out.println(plain.readData());
System.out.println("- Decoded --------------");
System.out.println(encoded.readData());
}
}
OutputDemo.txt: 실행 결과
- Input ----------------
Name,Salary
John Smith,100000
Steven Jobs,912000
- Encoded --------------
Zkt7e1Q5eU8yUm1Qe0ZsdHJ2VXp6dDBKVnhrUHtUe0sxRUYxQkJIdjVLTVZ0dVI5Q2IwOXFISmVUMU5rcENCQmdxRlByaD4+
- Decoded --------------
Name,Salary
John Smith,100000
Steven Jobs,912000