유니티/디자인패턴

[Unity] 유니티 C#) Decorator 패턴

새벽_글쓴이 2024. 12. 10. 10:25
반응형

데코레이터 패턴

객체에 동적으로 새로운 책임과 기능을 추가할 수 있게 해주는 구조적 디자인 패턴

 

기본구현방법

1. 기본 무기 인터페이스

public interface IWeapon
{
    float GetDamage();
    string GetDescription();
}

 

2.기본 무기 구현

public class BasicSword : IWeapon
{
    public float GetDamage()
    {
        return 10f;
    }

    public string GetDescription()
    {
        return "기본 검";
    }
}

 

3. 무기 데코레이터 기본 클래스

public abstract class WeaponDecorator : IWeapon
{
    protected IWeapon weapon;

    public WeaponDecorator(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public virtual float GetDamage()
    {
        return weapon.GetDamage();
    }

    public virtual string GetDescription()
    {
        return weapon.GetDescription();
    }
}

 

4. 화염 강화 데코레이터

public class FireEnhancement : WeaponDecorator
{
    public FireEnhancement(IWeapon weapon) : base(weapon) { }

    public override float GetDamage()
    {
        return base.GetDamage() + 5f; // 추가 화염 데미지
    }

    public override string GetDescription()
    {
        return base.GetDescription() + " + 화염 강화";
    }
}

 

5. 날카로움 강화 데코레이터

public class SharpnessEnhancement : WeaponDecorator
{
    public SharpnessEnhancement(IWeapon weapon) : base(weapon) { }

    public override float GetDamage()
    {
        return base.GetDamage() * 1.2f; // 20% 데미지 증가
    }

    public override string GetDescription()
    {
        return base.GetDescription() + " + 날카로움 강화";
    }
}

 

사용 예시

// 플레이어 클래스
public class Player : MonoBehaviour
{
    private IWeapon weapon;

    void Start()
    {
        // 기본 무기 생성
        weapon = new BasicSword();
        Debug.Log($"기본 무기: {weapon.GetDescription()}, 데미지: {weapon.GetDamage()}");

        // 화염 강화 추가
        weapon = new FireEnhancement(weapon);
        Debug.Log($"강화된 무기: {weapon.GetDescription()}, 데미지: {weapon.GetDamage()}");

        // 날카로움 강화 추가
        weapon = new SharpnessEnhancement(weapon);
        Debug.Log($"최종 무기: {weapon.GetDescription()}, 데미지: {weapon.GetDamage()}");
    }
}

 

핵심 개념

  • 기존 객체의 기능을 동적으로 확장/수정할 수 있게 해주는 구조적 패턴
  • 상속 대신 합성(Composition)을 사용하여 유연성 확보
  • 각 데코레이터는 기본 객체와 동일한 인터페이스를 구현
  • 여러 데코레이터를 조합하여 기능을 쌓아갈 수 있음

 

사용 목적

  • 런타임에 객체의 기능을 동적으로 추가/변경해야 할 때
  • 상속으로 처리하기에는 조합이 너무 많아질 때
  • 기존 코드를 수정하지 않고 새로운 기능을 추가하고 싶을 때
  • 객체의 책임과 기능을 분리하여 단일 책임 원칙을 지키고 싶을 때

 

사용 사례

  • 무기/장비 강화 시스템
  • 캐릭터 버프/디버프 시스템
  • 스킬 조합 시스템
  • UI 컴포넌트 확장

 

사용 사례 예시 코드

1. 무기/장비 강화 시스템

무기 시스템: 기본 무기에 강화 효과 추가
public interface IWeapon 
{
    float GetDamage();
    string GetDescription();
}

public class BasicSword : IWeapon 
{
    public float GetDamage() => 10f;
    public string GetDescription() => "기본 검";
}

public class EnhancementDecorator : IWeapon 
{
    protected IWeapon weapon;
    public EnhancementDecorator(IWeapon weapon) => this.weapon = weapon;
    public virtual float GetDamage() => weapon.GetDamage();
    public virtual string GetDescription() => weapon.GetDescription();
}

public class FireEnhancement : EnhancementDecorator 
{
    public FireEnhancement(IWeapon weapon) : base(weapon) {}
    public override float GetDamage() => base.GetDamage() + 5f;
    public override string GetDescription() => base.GetDescription() + " + 화염 강화";
}

// 사용 예시
IWeapon sword = new BasicSword();
sword = new FireEnhancement(sword);

 

2.캐릭터 버프 / 디버프 시스템

버프 시스템: 캐릭터 기본 능력치에 버프 효과 적용
public interface ICharacterStats 
{
    float GetAttack();
    float GetDefense();
}

public class BaseCharacter : ICharacterStats 
{
    public float GetAttack() => 10f;
    public float GetDefense() => 5f;
}

public class BuffDecorator : ICharacterStats 
{
    protected ICharacterStats character;
    public BuffDecorator(ICharacterStats character) => this.character = character;
    public virtual float GetAttack() => character.GetAttack();
    public virtual float GetDefense() => character.GetDefense();
}

public class StrengthBuff : BuffDecorator 
{
    public StrengthBuff(ICharacterStats character) : base(character) {}
    public override float GetAttack() => base.GetAttack() * 1.5f;
}

// 사용 예시
ICharacterStats player = new BaseCharacter();
player = new StrengthBuff(player);

 

3. 스킬 조합 시스템

스킬 시스템: 기본 스킬에 추가 효과 조합
public interface ISkill 
{
    float GetDamage();
    string GetEffect();
}

public class BaseFireball : ISkill 
{
    public float GetDamage() => 20f;
    public string GetEffect() => "화염 데미지";
}

public class SkillDecorator : ISkill 
{
    protected ISkill skill;
    public SkillDecorator(ISkill skill) => this.skill = skill;
    public virtual float GetDamage() => skill.GetDamage();
    public virtual string GetEffect() => skill.GetEffect();
}

public class IceEffect : SkillDecorator 
{
    public IceEffect(ISkill skill) : base(skill) {}
    public override string GetEffect() => base.GetEffect() + " + 빙결 효과";
}

// 사용 예시
ISkill fireball = new BaseFireball();
fireball = new IceEffect(fireball); // 얼음 화염구

 

4. UI 컴포넌트 확장

UI 시스템: 기본 UI 요소에 시각 효과 추가
public interface IUIElement 
{
    void Render();
    string GetStyle();
}

public class BasicButton : IUIElement 
{
    public void Render() => Debug.Log("기본 버튼 렌더링");
    public string GetStyle() => "기본 스타일";
}

public class UIDecorator : IUIElement 
{
    protected IUIElement element;
    public UIDecorator(IUIElement element) => this.element = element;
    public virtual void Render() => element.Render();
    public virtual string GetStyle() => element.GetStyle();
}

public class AnimationDecorator : UIDecorator 
{
    public AnimationDecorator(IUIElement element) : base(element) {}
    public override void Render()
    {
        base.Render();
        Debug.Log("애니메이션 효과 추가");
    }
}

// 사용 예시
IUIElement button = new BasicButton();
button = new AnimationDecorator(button);

 

결론

장점

  • 기능 확장이 유연하고 동적임
  • 각 기능을 분리하여 관리 용이
  • 기존 코드 수정 없이 새 기능 추가 가능
  • 여러 기능을 조합하여 사용 가능

 

단점

  • 데코레이터 클래스가 많아지면 복잡도 증가
  • 순서에 따라 결과가 달라질 수 있음
  • 작은 객체들이 많이 생성될 수 있음

 

데코레이터 패턴은 동적으로 기능 추가가 가능하고 기능의 조합이 자유롭다
또한, 코드 재사용성이 높고 확장이 용이하기에 유연하고 확장 가능한

시스템을 구축할 수 있을 것 같다
반응형