데코레이터 패턴

ynkim·2024년 12월 26일
0

데코레이터

객체들을 새로운 행동을 포함한 특수 래퍼 객체 내에 넣어서 위 행동들을 해당 객체에 연결시키는 패턴

구조

  • 컴포넌트: 래퍼들과 래핑된 객체들 모두에 대한 공통 인터페이스를 선언
  • 구상 컴포넌트: 기본 행동들을 정의하고 해당 기본 행동들은 데코레이터들이 변경 가능
  • 기초 데코레이터: 래핑된 객체를 참조하기 위한 필드가 존재하며 이 필드는 컴포넌트 인터페이스로 선언
  • 구상 데코레이터: 컴포넌트에 동적으로 추가될 수 있는 추가 행동들을 정의

예시 코드

// Component 인터페이스
interface DataSource {
    void writeData(String data);
    String readData();
}

// Concrete Component 클래스
class FileDataSource implements DataSource {
    private String filename;

    public FileDataSource(String filename) {
        this.filename = filename;
    }

    @Override
    public void writeData(String data) {
        System.out.println("Writing data to file: " + filename);
    }

    @Override
    public String readData() {
        System.out.println("Reading data from file: " + filename);
        return "File data";
    }
}

// Base Decorator 클래스
class DataSourceDecorator implements DataSource {
    protected DataSource wrappee;

    public DataSourceDecorator(DataSource source) {
        this.wrappee = source;
    }

    @Override
    public void writeData(String data) {
        wrappee.writeData(data);
    }

    @Override
    public String readData() {
        return wrappee.readData();
    }
}

// Concrete Decorator - Encryption
class EncryptionDecorator extends DataSourceDecorator {

    public EncryptionDecorator(DataSource source) {
        super(source);
    }

    @Override
    public void writeData(String data) {
        String encryptedData = encrypt(data);
        System.out.println("Encrypting data.");
        super.writeData(encryptedData);
    }

    @Override
    public String readData() {
        String data = super.readData();
        String decryptedData = decrypt(data);
        System.out.println("Decrypting data.");
        return decryptedData;
    }

    private String encrypt(String data) {
        return "Encrypted(" + data + ")";
    }

    private String decrypt(String data) {
        return data.replace("Encrypted(", "").replace(")", "");
    }
}

// Concrete Decorator - Compression
class CompressionDecorator extends DataSourceDecorator {

    public CompressionDecorator(DataSource source) {
        super(source);
    }

    @Override
    public void writeData(String data) {
        String compressedData = compress(data);
        System.out.println("Compressing data.");
        super.writeData(compressedData);
    }

    @Override
    public String readData() {
        String data = super.readData();
        String decompressedData = decompress(data);
        System.out.println("Decompressing data.");
        return decompressedData;
    }

    private String compress(String data) {
        return "Compressed(" + data + ")";
    }

    private String decompress(String data) {
        return data.replace("Compressed(", "").replace(")", "");
    }
}

// Client 코드
class Application {
    public static void main(String[] args) {
        DataSource source = new FileDataSource("somefile.dat");

        // Compression 추가
        source = new CompressionDecorator(source);

        // Encryption 추가
        source = new EncryptionDecorator(source);

        // 데이터 쓰기
        source.writeData("Salary Records");

        // 데이터 읽기
        String result = source.readData();
        System.out.println("Final Data: " + result);
    }
}
  1. 비즈니스 도메인이 기본 컴포넌트로 표시될 수 있는지 확인한다.
  2. 기본 컴포넌트와 선택적 계층들에 공통적인 메서드를 파악하고 컴포넌트 인터페이스를 만들어 선언한다.
  3. 구상 컴포넌트 클래스를 만들고 기초 행동들을 정의한다.
  4. 기초 데코레이터 클래스를 만들고 모든 작업을 래핑된 객체에 위임한다.
  5. 모든 클래스들이 컴포넌트 인터페이스를 구현한다.
  6. 기초 데코레이터를 확장하여 구상 데코레이터를 생성한다. 구상 데코레이터는 부모 메서드 호출 전 혹은 후에 행동들을 실행해야 한다.

장단점

장점

  • 새 자식 클래스를 만들지 않아도 객체의 행동을 확장 가능
  • 런타임에 객체들에서 책임을 추가/제거 가능
  • 객체를 여러 데코레이터로 래핑하여 여러 행동들을 합성 가능
  • 단일 책임 원칙: 여러 개의 작은 클래스로 나눌 수 있음
    단점
  • 래퍼들의 스택에서 특정 래퍼를 제거하기 어려움
  • 데코레이터의 행동이 스택 내의 순서에 의존하지 않는 방식으로 구현하기 어려움
  • 계층들의 초기 설정 코드가 보기 흉할 수 있음

0개의 댓글