본문 바로가기
프로그래밍/디자인패턴

Iterator Pattern

by 리뷰하는 (게임)프로그래머_리프TV 2016. 5. 4.

 

이번에는 Iterator 패턴에 대해 알아보고, 

해당 부분을 적용한 Unity 예제와, 조금의 응용을 같이 진행에 보도록 하겠습니다. 

 

흔히 Iterator라 함은, 구지 디자인 패턴의 종류라기보단 컨테니어들을 따라갈때 많이 쓰던 키워드 중 하나? 정도로 많이 파악이 되어 있습니다. 실제로 그것과 크게 다르지도 않습니다.

 

중요한 것은, 특정 List나 Arr등 그룹지어져 있는 Object들을 순차적( 구현에 따라선 특정 구간이 될 수도... )으로 순회하면서 찾는다는 것이 중요합니다. 단지 이것을 디자인 패턴으로 표현 하였을 때 어떤식의 장점이 있는지, 그리고 어떤식으로 구현해야 하는지 확인해 본다고 보시면 될 것 같습니다.

 

먼저 최종 사용 코드부터 보도록 하겠습니다. 

 

사용 코드 예제

 

namespace IteratorPattern
{
    public class TestPattern : MonoBehaviour
    {
        // 데이터 삽입을 위해 싱글톤으로 두었습니다.
        public static TestPattern instance;

        BookShelf bookShelf = new BookShelf();  // 실제 데이터가 들어갈 서가
        BookShelfIterator bookIterator;         // 서가을 순회할 Iterator

        void Awake()
        {
            instance = this;
        }

        void Start()
        {
            // Iterrator 를 하나 생성합니다.
            bookIterator = new BookShelfIterator( bookShelf );

            Debug.Log("책장을 검색합니다.");

            // 전체 검색 시작
            while (bookIterator.hasNext())
            {
                // 찾아와 정보를 표현합니다.
                Book book = (Book)bookIterator.next();
                Debug.Log(book.getName() + " 이 존재합니다.");
            }

            Debug.Log("검색이 완료되었습니다.");
        }

        // BookShelf(책장)에 책을 저장합니다.
        public void Addbook(Book book)
        {
            bookShelf.appendBook(book);
        }
    }
}​

 

(*사용 구현에 대해서는 주관적인 부분입니다.)

 

코드를 보면 BookShelf에 Book이라는 Class를 넣어 주고, BookShelfIterator를 사용하여 순회 합니다.

그럼 다음으로 실제로 저장 될( 서가에 들어갈 ) Book Class를 보도록 하겠습니다.

 

namespace IteratorPattern
{
    // 서가에 들어갈 책 class
    public class Book : MonoBehaviour
    {
        public string bookName;

        public string getName()
        {
            return bookName;
        }

        void Start()
        {
            // 생성 될 때 자동으로 서가에 넣어 줍니다.
            TestPattern.instance.Addbook(this);
            Debug.Log(bookName + "을 책장에 삽입");
        }
    }
}​

아직 Iterator Pattern에서 어떤 처리를 하는지는 설명하지 않았으나,

원하는 결과는 다음과 같을 것입니다.

 

GameObecjt를 2개 추가하여, 각각 Script를 지정합니다.

 

Iterator Pattern 을 넣고

 

국어책도 한권 넣어서 실행하면,

국어책을 책장에 넣고, 책장을 순회하여 어떤 책이 있는지 확인해 볼 수 있습니다.

 

책의 갯수를 조금 더 늘려 보도록 하겠습니다. 

 

 

 

3권을 더 추가하였습니다.

 

각각의 책을 서가에 넣고,

 

서가을 순회하면서 모든 책들을 찾아 보았습니다.

 

그럼 이제 실제 패턴에 구현 부분을 확인해 보도록 하겠습니다. 

 

namespace IteratorPattern
{
    // Aggregate interface
    public interface Aggregate
    {
        Iterator iterator();
    }

    // iterator 인터페이스
    public interface Iterator
    {
        bool hasNext();
        Object next();
    }


    // 책들이 보관되어 있는 서가 Aggregate Interface를 상속받습니다.
    public class BookShelf : Aggregate
    {
        private List<Book> books = new List<Book>();

        // index로 찾기, 예외 처리는 패스
        public Book getBookAt( int index )
        {
            return books[index];
        }

        // 새로운 책을 서가에 넣기
        public void appendBook( Book book )
        {
            this.books.Add(book);
        }
        
        public int getLength()
        {
            return books.Count;
        }

        // Aggregate 인터페이스를 상속 받았기 때문에 itorator() 를 꼭 추가 해야합니다.
        public Iterator iterator()
        {
            // return type이 Iterator 인 것이 중요합니다.
            return new BookShelfIterator( this );
        }
    }

    // Iterator 를 상속받은 클래스
    public class BookShelfIterator : Iterator
    {
        // BookShelfIterator 안에 BookShelf가 포함 되어 있습니다.
        private BookShelf bookShelf;
        private int index;

        public BookShelfIterator( BookShelf bookShelf )
        {
            this.bookShelf = bookShelf;
            this.index = 0;
        }

        public bool hasNext()
        {
            if( index < bookShelf.getLength() )
            {
                return true;
            }
            return false;
        }

        // unity Object를 return
        public Object next()
        {
            Book book = bookShelf.getBookAt( index );
            ++index;
            
            // 중요한것은 book Type이 아닌 Object라는 것 입니다.
            return (Object)book;
        }
    }
}​

 

코드를 보면 BookShelf(서가)클래스 안에 Book(책)을 담을 수 있어야 하고.

서가에 iterator를 통해 서가을 검색, 순차적으로 접근 할 수 있어야 합니다.

그런 실제 iterator를 구현한 클래스가 BookShelfIiterator class 입니다.

 

여기 까지가 iterator pattern의 기본 예제였습니다.

 

 

여기서 조금 응용을 해보겠습니다.

 

* 만약 기존의 책이 아닌 사진을 저장해 놓은 앨범이라는 새로운 서적이 추가되었다. 그렇다면 여기서 어떻게 진행해야 할까?

 

첫번째로 앨범 전용 서가를 하나 추가할 수도 있을 것이고,

Book Class 처럼 Album이라는 class를 새로 만들 수도 있을 것입니다. 

Iterator도 Album 전용 Iterator가 생성 될 수도 있고, 기존의 Iterator를 활용하여 진행 할 수도 있을 것입니다. 

 

저는 해당 문제를 기존의 Iterator Pattern을 그대로 두면서 Book Class를 활용하는 방법으로 진행하도록 하겠습니다. 

 

다른 부분은 다 그대로 두고 Album 이라는 class를 추가 해 보도록 하겠습니다. 

 

namespace IteratorPattern
{
    // Book Class를 상속 받은 Album을 만들어 보았습니다.
    public class Album : Book
    {
        // 사진이 몇장이나 들어 있나?
        public int pictureCount;

        public int getPictureCount()
        {
            return pictureCount;
        }

        void Start()
        {
            // 서가에 앨범을 추가합니다.
            TestPattern.instance.Addbook(this);
            Debug.Log(pictureCount + "사진이 들어있는 앨범을 책장에 삽입");
        }
    }
}​

Book Class를 상속받아서 편하게 추가해 보았습니다. 

하지만 꼭 이 방법만이 있는것은 아니겠죠.

 

 

이런식으로 접근해서 해결 하였지만,

 

 

이런 방법도 있을 수 있을 것 같습니다.

(하지만 이렇게 하면 BookShelf 에서 더 코드변경이 일어날지도 모르겠네요)

 

 

조금 복잡 할 수도 있으나 Iterator Pattern을 쓰는 이유는 아마 이런 이유가 아닐까 싶습니다.

 

새로운 무언가( Obecjt, Class ... ) 가 추가 되었을 때 기존의 검색 체계에서 벗어나지 않게 쉽게 추가 할 수 있기 위해서라고 생각합니다. 

 

즉, 구현에 상관 없이 Iterator를 사용할 수 있기 때문.

 

Alnum을 추가 하고 실행하면 다음과 같이 표현됩니다.

 

 

 

 

이상으로 Iterator Pattern에 대한 설명을 마칩니다. 많이 부족하지만, 점점 나아질 것이라 생각합니다.