공부합시다/DesignPattern

[Design Pattern] 데코레이터(Decorator) 패턴

Lede_ 2023. 4. 5. 06:25

데코레이터(Decorator) 패턴이란?

  • 전체 클래스나 하위 클래스를 수정하지 않고도 개별 개체에 동작이나 기능을 추가할 수 있도록 한다.
  • 런타임에 개체의 기능을 동적으로 확장하는데 사용한다.

데코레이터 패턴의 장점

확장을 위한 개방

  • 기존 코드를 수정하지 않고 객체의 동작을 동적으로 수정하고 확장할 수 있다.

단일 책임 원칙

  • 객체의 책임을 각각 다른 데코레이터 객체로 분리하여 개체의 동작을 유연하게 관리할 수 있다.

상속보다 구성

  • 부모 클래스에서 동작을 상속하는 대신 새로운 개체에 동작을 추가하기 때문에 유지보수 및 수정에 유연성이 생기고 확장하기 좋다.

간소화된 유지보수

  • 기존의 개체를 수정하지 않고 데코레이터 개체를 사용하여 새로운 동작을 추가할 수 있기 때문에 기존 코드에 영향을 주지 않고 개체의 동작을 변경할 수 있다.

데코레이터 패턴의 단점

복잡성 증가

  • 많은 수의 데코레이터가 사용되는 경우 코드를 이해하고 유지보수하는데 문제가 발생할 수 있다.

성능

  • 데코레이터는 객체를 래핑(Wrapping)하여 생성하므로 데코레이터의 생성 및 관리에서 성능의 문제가 발생할 수 있다.

구현 예제

다음은 게임의 스킬 체인 시스템을 예로 들어 설명한 데코레이터 패턴의 구현 예제이다.
먼저 데코레이터 클래스를 정의한다.

// 캐릭터의 기본 인터페이스
public interface Character {
    void attack();
    int getHealth();
}

// 캐릭터, Charactor 인터페이스의 구현체
public class Player implements Charactor {
    private int health = 100;

    public void attack() {
        System.out.println("Attack.");
    }

    public int getHealth() {
        return health;
    }
}

// Charactor 인터페이스를 정의하는 Decorator 추상 클래스
public abstract class CharacterDecorator implements Character {
    protected Character character; // 데코레이터로 장식할 객체

    public CharacterDecorator(Character character) {
        this.character = character;
    }

    // 캐릭터의 공격 방법을 장식된 객체에 위임
    public void attack() {
        character.attack();
    }

    // 캐릭터의 getHealth() 메소드 또한 장식된 객체에 위임
    public int getHealth() {
        return character.getHealth();
    }
}

// 캐스팅하는 마법을 추가하는 구체적인 데코레이터 클래스
public class MagicDecorator extends CharacterDecorator {
    public MagicDecorator(Character character) {
        super(character);
    }

    // 공격 방법을 재정의하여 마법 공격을 추가한다.
    public void attack() {
        character.attack();
        System.out.println("Character casts a magic spell!");
    }
}

// 화염 속성 공격을 추가하는 데코레이터 클래스
public class FireDecorator extends CharacterDecorator {
    public FireDecorator(Character character) {
        super(character);
    }

    // 공격 방법을 재정의하여 화염 속성을 추가한다.
    public void attack() {
        character.attack();
        System.out.println("Character casts a magic with fire!");
    }
}

다음은 플레이어를 생성하고 데코레이터를 추가하여 공격 방식을 변경하는 예제이다.

public static void main(String[] args) {
    // 장식이 될 플레이어 객체 생성
    Character player = new Player();

    // 플레이어가 마법을 캐스팅하는 데코레이터 추가
    Character magicCaster = new MagicDecorator(player);

    // 플레이어의 마법에 화염 속성을 추가하는 데코레이터 추가
    Character fireMagicCaster = new FireDecorator(magicCaster);

    // 플레이어가 공격한다면
    player.attack(); // 출력: Attack.
    magicCaster.attack(); // 출력: Attack.\n Character casts a magic spell!
    fireMagicCaster.attack(); // 출력: Attack.\n Character casts a magic spell!\n Character casts a magic with fire!
}