게임 개발/디자인 패턴

[디자인패턴] 12. 유니티에서 컴포지트 패턴 feat.이터레이터 패턴

Heesuk Lee 2021. 12. 25. 19:55

이번 챕터에서는 컴포지트 패턴을 이터레이터 패턴에 이어서 같은 주제, monster info와 weapon info로 소개하겠습니다.

 

컴포지트 패턴의 정의

객체들을 트리구조로 구성하여 부분 - 전체 계층구조를 구현합니다.

컴포지트 패턴을 이용하면 개별객체와 복합객체를 똑같은 방법으로 다룰 수 있게 됩니다.

 

이전시간에 배운 이터레이터 패턴과 혼합하여 설명하자면 복합객체인 iterator와 개별객체인 item이 구분할 필요가 없어,

item을 가진 iterator를 가진 그위에 iterator를 가지는 형태, 즉 트리구조로 복합객체를 만들어갈 수 있습니다.

 

복합객체와 개별객체가 상속받을 infoComponent를 만들었습니다.

복합객체와 개별객체의 구분을 없애줄 것입니다.

public abstract class InfoComponent
{
    public virtual InfoComponent GetChild(int _index)
    {
        return null;
    }

    public virtual string GetName()
    {
        return string.Empty;
    }
    public virtual string GetDescription()
    {
        return string.Empty;
    }
    public virtual void Print()
    {
        //
    }
}

 

이터레이터 내부에 들어가며 info component를 상속받는 monster info와 weapon info입니다

public class MonsterInfo : InfoComponent
{
    public string name = string.Empty;
    public int hp = 0;

    public MonsterInfo(string _name, int _hp)
    {
        this.name = _name;
        this.hp = _hp;
    }
    public override string GetName()
    {
        string monsterName = $"이름 : {this.name}";
        return monsterName;
    }
    public override string GetDescription()
    {
        string description = $"체력 : {this.hp}";
        return description;
    }
    public override void Print()
    {
        Debug.Log($"{this.GetName()}/{this.GetDescription()}");
    }
}


public class WeaponInfo : InfoComponent
{
    public string name = string.Empty;
    public int damage = 0;

    public WeaponInfo(string _name, int _damage)
    {
        this.name = _name;
        this.damage = _damage;
    }
    public override string GetName()
    {
        string monsterName = $"이름 : {this.name}";
        return monsterName;
    }
    public override string GetDescription()
    {
        string description = $"공격력 : {this.damage}";
        return description;
    }
    public override void Print()
    {
        Debug.Log($"{this.GetName()}/{this.GetDescription()}");
    }
}

 

그리고 이터레이터로 사용했던 monsteriterator, weaponIterator또한 info component를 상속받아

함수들을 구현해줍니다. 위에 info들과 다른점은 iterator 내부 info들의 print를 호출해주도록 Print를 구현해줍니다.

public class MonsterIterator : InfoComponent, Iterator
{
    private List<InfoComponent> infos = new List<InfoComponent>();
    private int index = -1;

    public string name = string.Empty;
    public string description = string.Empty;

    public MonsterIterator(string _name, string _description)
    {
        this.name = _name;
        this.description = _description;

        this.index = 0;
    }

    public void Add(InfoComponent _infoComponent)
    {
        this.infos.Add(_infoComponent);
    }

    public bool HasNext()
    {
        bool _hasNext = this.index < this.infos.Count;
        return _hasNext;
    }

    public InfoComponent Next()
    {
        return this.infos[this.index++];
    }

    public void Remove(InfoComponent _target)
    {
        this.infos.Remove(_target);
    }

    public override void Print()
    {
        Debug.Log($"--------------------------------------------");
        Debug.Log($"{this.GetName()}/{this.GetDescription()}");
        Debug.Log($"--------------------------------------------");

        while (this.HasNext())
        {
             this.Next().Print();
        }
    }

    public override string GetName()
    {
        return this.name;
    }
    public override string GetDescription()
    {
        return this.description;
    }
}


public class WeaponIterator : InfoComponent, Iterator
{
    private InfoComponent[] infos = {};
    private int index = -1;

    public string name = string.Empty;
    public string description = string.Empty;


    public WeaponIterator(string _name, string _description)
    {
        this.name = _name;
        this.description = _description;

        this.index = -1;
    }

    public void Add(InfoComponent _infoComponent)
    {
        InfoComponent[] beforeArray = this.infos;
        this.infos = new InfoComponent[this.infos.Length + 1];
        
        int count = 0;
        while (count >= beforeArray.Length)
        {
            this.infos[count] = beforeArray[count];
            count++;
        }
        this.infos[count] = _infoComponent;
    }

    public bool HasNext()
    {
        bool _hasNext = this.index < this.infos.Length;
        return _hasNext;
    }

    public InfoComponent Next()
    {
        return this.infos[this.index++];
    }

    public void Remove(InfoComponent _target)
    {
        // this.infos.Remove(_target);
    }

    public override void Print()
    {
        Debug.Log($"--------------------------------------------");
        Debug.Log($"{this.GetName()}/{this.GetDescription()}");
        Debug.Log($"--------------------------------------------");
        
        while (this.HasNext())
        {
             this.Next().Print();
        }
    }

    public override string GetName()
    {
        return this.name;
    }
    public override string GetDescription()
    {
        return this.description;
    }
}

 

마지막으로 실행부입니다. 

public class IteratorCheck : MonoBehaviour
{
    private List<Iterator> iterators = new List<Iterator>();
    
    private MonsterInfo[] monsterInfos = {new MonsterInfo("고블린", 1), new MonsterInfo("스켈레톤", 2), new MonsterInfo("오크", 3), new MonsterInfo("드래곤", 4)};
    private WeaponInfo[] weaponInfos = {new WeaponInfo("검", 1), new WeaponInfo("도끼", 2), new WeaponInfo("화살", 3), new WeaponInfo("창", 4)};
    
    void Start()
    {
        this.SetIterator("몬스터 리스트", "몬스터 정보를 출력해줍시다.", this.monsterInfos);
        this.SetIterator("무기 리스트", "무기 정보를 출력해줍시다.", this.weaponInfos);
        
        this.PrintInfos();
    }

    private void SetIterator(string _name, string _description, InfoComponent[] _infos)
    {
        Iterator iterator = new MonsterIterator(_name, _description);
        for (int i = 0; i < _infos.Length; i++)
        {
            iterator.Add(_infos[i]);
        }
        this.iterators.Add(iterator);
    }

    void PrintInfos()
    {
        for (int i = 0; i < this.iterators.Count; i++)
        {
            InfoComponent infoComponent = (InfoComponent)this.iterators[i];
            infoComponent.Print();
        }
    }
}

 

 

로그로 나온 결과로써 Print함수 하나로 다수의 iterator와 그 내부에 있는 info들의 속성들을 순차적으로 호출되는것을 확인할 수 있었습니다.

 

모두 같은 info component를 상속받아 순서대로 출력되는 모습

 

안정성부분에서 살짝 위험하다고 하지만 뭔가 신기한 느낌의 패턴이었다.

존재한다는 것만 알아두면 될 것 같다... 게임 구현에 있어서 좋은 파트가 생각나질 않는다.

 

반응형