특정 DataSource의 인터페이스가 있고 그 구현체로 FileDataSource가 있다고해보자 그런데 파일데이터를 압축해달라는 요구사항이 생기고 차례로 암호화, 암호화와 압축화를 동시에 해달라는 요구사항이 생겼을 때 아래와 같이 상속을 이용하여 해결할 수 있을 것이다.
Copy public interface DataSource {
void writeData ( String data);
String readData ();
}
public class FileDataSource implements DataSource {
private String fileName;
public FileDataSource ( String fileName) {
this . fileName = fileName;
}
@ Override
public void writeData ( String data) {
System . out . println (fileName + "에 " + data + "쓰기" );
}
@ Override
public String readData () {
return "data" ;
}
}
public class CompressionFileDataSource extends FileDataSource {
public CompressionFileDataSource ( String fileName) {
super(fileName);
}
@ Override
public void writeData ( String data) {
super . writeData ( "압축 " + data);
}
@ Override
public String readData () {
String data = super . readData ();
return "압축해제 " + data;
}
}
public class EncryptionFileDataSource extends FileDataSource {
public EncryptionFileDataSource ( String fileName) {
super(fileName);
}
@ Override
public void writeData ( String data) {
super . writeData ( "암호화 " + data);
}
@ Override
public String readData () {
String data = super . readData ();
return "복호화 " + data;
}
}
public class EncryptionCompressionFileDataSource extends FileDataSource {
public EncryptionCompressionFileDataSource ( String fileName) {
super(fileName);
}
@ Override
public void writeData ( String data) {
super . writeData ( "암호화 압축 " + data);
}
@ Override
public String readData () {
String data = super . readData ();
return "복호화 압축해제 " + data;
}
}
public class App {
public static void main ( String [] args) {
String fileName = "README.md" ;
String data = "데코레이터" ;
DataSource dataSource = new FileDataSource(fileName) ;
dataSource . writeData (data); //README.md에 데코레이터쓰기
System . out . println ( dataSource . readData ()); //data
dataSource = new CompressionFileDataSource(fileName) ;
dataSource . writeData (data); //README.md에 압축 데코레이터쓰기
System . out . println ( dataSource . readData ()); //압축해제 data
dataSource = new EncryptionFileDataSource(fileName) ;
dataSource . writeData (data); //README.md에 암호화 데코레이터쓰기
System . out . println ( dataSource . readData ()); //복호화 data
dataSource = new EncryptionCompressionFileDataSource(fileName) ;
dataSource . writeData (data); //README.md에 암호화 압축 데코레이터쓰기
System . out . println ( dataSource . readData ()); //복호화 압축해제 data
}
}
그런데 위와 같은 상속을 통해 기능을 확장하는 방식은 구현체가 많이 존재하는데 추가하려는 기능이 많아질 수록 문제가 발생한다. 만일 DataSource의 구현체가 DbDataSource / CacheDataSource ... 와 같이 다양하게 존재하고 해당 구현체마다 기능들을 추가하려고 한다면 생성할 클래스의 갯수가 NxM 갯수만큼 증가하기 때문이다.
이를 해결하기 위한 방법으로 데코레이터 패턴을 이용할 수 있다.
Copy public class Decorator implements DataSource {
private DataSource dataSource;
public Decorator ( DataSource dataSource) {
this . dataSource = dataSource;
}
@ Override
public void writeData ( String data) {
dataSource . writeData (data);
}
@ Override
public String readData () {
return dataSource . readData ();
}
}
public class CompressionDecorator extends Decorator {
public CompressionDecorator ( DataSource dataSource) {
super(dataSource);
}
@ Override
public void writeData ( String data) {
super . writeData ( "압축 " + data);
}
@ Override
public String readData () {
String data = super . readData ();
return "압축해제 " + data;
}
}
public class EncryptionDecorator extends Decorator {
public EncryptionDecorator ( DataSource dataSource) {
super(dataSource);
}
@ Override
public void writeData ( String data) {
super . writeData ( "암호화 " + data);
}
@ Override
public String readData () {
String data = super . readData ();
return "복호화 " + data;
}
}
public class App {
boolean compressionFlag = true ;
boolean encryptionFlag = true ;
public static void main ( String [] args) {
String fileName = "README.md" ;
String data = "\'데코레이터\'" ;
DataSource dataSource = new FileDataSource(fileName) ;
if (compressionFlag){
dataSource = new CompressionDecorator(dataSource) ;
}
if (encryptionFlag){
dataSource = new EncryptionDecorator(dataSource) ;
}
dataSource . writeData (data); //README.md에 암호화 압축 '데코레이터' 쓰기
}
}
데코레이터를 이용하는 객체가 많지 않은 현재 예제같은 경우에는 생성한 클래스의 개수가 비슷해 보일 수 있지만 객체가 많아진다면 첫번째 예제처럼 NxM이 아닌 N+M의 갯수만큼만 생성해주면 된다.