게임 개발/디자인 패턴

[디자인 패턴] 11. 유니티에서 이터레이터 패턴

Heesuk Lee 2021. 11. 9. 20:40

 

이번에는 이터레이터 패턴, 반복자 패턴입니다.

 

인간은 어쩔 수 없이 반복을 한다.

 

컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식이 통일시킨다면 어떤 종류의 집합체에 대해서도 사용할 수 있는 하나의 함수로 모든 컬렉션을 다룰수있게됩니다.

 

핵심은 다양한 집합체를 하나의 클래스로 상속받아 관리하며 같은 함수로 내부에 접근을 할 수 있다는 것입니다.

넓게보자면 이전에 포스팅했었던 어댑터패턴과 관통하는 부분이 있을 수 있습니다.

 

2021.09.30 - [게임 개발/디자인 패턴] - [디자인패턴] 9. 유니티에서의 어댑터 패턴 퍼사드 패턴

 

[디자인패턴] 9. 유니티에서의 어댑터 패턴 퍼사드 패턴

이번 챕터에서는 두개의 패턴이 함께 있어서 같이 작성했습니다. 첫번째로 어댑터 패턴입니다. 어댑터 패턴 정의 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스

welcomeheesuk.tistory.com

 

가장 기반이 될 Iterator 인터페이스와 저장될 info클래스를 먼저 살펴봅시다.

public interface Iterator 
{
	// 다음 index에 요소가 있는지 체크
    bool HasNext();
    // 다음 index의 요소를 반환
    Info Next();
    // 매개변수에 해당되는 요소를 제거
    void Remove(Info _target);
    // 전체 길이를 반환
    int GetLength();
    // 원하는 메소드를 추가해도 된다.
}

// 임의적으로 필요한 요소를 클래스로 만들어 상속시킬 것 입니다.
public class Info
{
    public string name = string.Empty;
    public int hp = 0;
    public int damage = 0;
}

 

일부러 하나는 List 하나는 배열로 만들어 같은 함수를 사용할 수 있게 구현할 것 입니다.

 

첫번째로 List로 정리되는 MonsterIterator입니다.

 

public class MonsterInfo : Info
{
	// info를 상속받아 생성자에서 정보 저장
    public MonsterInfo(string _name, int _hp)
    {
        this.name = _name;
        this.hp = _hp;
    }
}

public class MonsterIterator : Iterator
{
	// 몬스터 정보리스트
    private List<Info> infos = new List<Info>();
    // 인덱스
    private int index = -1;
	
    // 다양한 방법이 있지만 빠른 테스트를 위해 배열을 받아 초기화시켜줬습니다.
    public MonsterIterator(MonsterInfo[] _array)
    {
        for (int i = 0; i < _array.Length; i++)
        {
            this.infos.Add(_array[i]);
        }
        this.index = 0;
    }

    // 다음 인덱스에 요소가 있는가
    public bool HasNext()
    {
        bool _hasNext = this.index < this.infos.Count;
        return _hasNext;
    }

    // 다음 인덱스의 요소를 반환
    public Info Next()
    {
        return this.infos[this.index++];
    }
    // 매개변수로 받은 요소를 제거
    public void Remove(Info _target)
    {
        this.infos.Remove(_target);
    }
	// 전체 길이 반환
    public int GetLength()
    {
        return this.infos.Count;
    }
}

 

다음은 배열로 정리될 WeaponIterator입니다.

 

public class WeaponInfo : Info
{
	// info를 상속받아 생성자에서 정보 저장
    public WeaponInfo(string _name, int _damage)
    {
        this.name = _name;
        this.damage = _damage;
    }
}

public class WeaponIterator : Iterator
{
	// 무기 정보리스트
    private Info[] infos = {};
    // 인덱스
    private int index = -1;

    public WeaponIterator(WeaponInfo[] _array)
    {
        this.infos = _array;
        this.index = 0;
    }

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

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

    public void Remove(Info _target)
    {
        // this.infos.Remove(_target);
    }
    public int GetLength()
    {
        return this.infos.Length;
    }
}

 

 

마지막으로 이터레이터패턴을 활용하기위해 간단하게 iteratorCheck 클래스를 만들었습니다.

실제 구현부입니다.

 

 

public class IteratorCheck : MonoBehaviour
{
    private MonsterIterator monsterIterator = null;
    private WeaponIterator weaponIterator = null;
    
    // 예시를 위한 처음 정보배열들
    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()
    {
    	// 생성과 동시에 iterator에 배열 저장합니다.
        this.monsterIterator = new MonsterIterator(this.monsterInfos);
        this.weaponIterator = new WeaponIterator(this.weaponInfos);
        // iterator를 상속받은 monsterIterator, weaponIterator는 같은 함수로 편하게 사용할 수 있다.
        this.ShowIterator(this.monsterIterator);
        this.ShowIterator(this.weaponIterator);
        
    }

    void ShowIterator(Iterator _iterator)
    {
    	// 매개변수로 받은 iterator를 순회하며 정보를 출력해줍니다.
        for (int i = 0; i < _iterator.GetLength(); i++)
        {
            Info info = _iterator.Next();
            Debug.Log($"이름: {info.name}, 체력:{info.hp}, 공격력:{info.damage} / 다음 정보 존재: {_iterator.HasNext()}");
        }
    }
}

 

 

위와같이 구현하고 실행하게되면 아래와 같이 디버그로그가 찍히게 됩니다.

각기 다른 배열 혹은 리스트를 하나의 함수로 요소를 호출시킬수있는건 큰 이점인 것 같습니다!

 

List로 정리된 Monster와 array로 정리된 weapon을 같은 함수로 결과를 도출했다!

 

반응형