프로그램을 작성하다보면 여러 개로 구성되는 자료구조(예를 들어 배열, 트리, 리스트 등)에 대해 각각의 요소에 접근해서 필요한 작업을 수행하는 일이 빈번히 일어납니다. 각 자료구조 별로 요소에 접근하는 방법이 다를 수 있기 때문에 항상 자료구조에 맞춰 각 요소에 접근하는 코드를 작성해줘야 합니다. 이런 자료구조에 상관없이 일관되게 적용할 수 있는 방법을 소개합니다.
예를 들어 리스트를 구성하는 각 자료 항목들을 순차적으로 접근하려고 할 때 리스트가 다음 항목이 어떤 것인지 모를 때도 접근하려면 리스트 자체를 클래스로 정의할 수 있습니다. 리스트 자체를 클래스로 정의해서 내부의 자료구조를 외부에 숨기도록 즉 자료구조에 상관없이 사용하게 할 수 있습니다. 하지만 이렇게 될 경우 자료구조 자체를 클래스로 정의해서 사용하기 때문에 그 클래스에서는 추가, 삭제, 등등 자료구조들이 가져야할 기능들을 인터페이스로 제공해야 합니다. 그리고 하나의 객체에서 동시에 두 곳 이상의 다른 위치 항목에 접근하는 것이 불가능해집니다.
위 문제들을 해결하기 위해 두번째 문제인 동시에 두 곳이상 접근 문제 해결을 먼저 하기 위해 리스트 클래스에서 위치 정보를 저장하여 관리하는 클래스를 분리합니다. 그렇게 되면 아래와 같은 구조를 가집니다. ListIterator 클래스는 생성시에 List 클래스의 객체를 지정하고 최소한의 인터페이스를 제공합니다.
위처럼 실제 자료구조를 구성하는 클래스와는 별도로 클래스 객체의 항목들을 순차적으로 접근하기 위한 클래스를 정의해서 사용하는 방식을 이터레이터 패턴이라고 합니다. 이터레이터 패턴을 사용하면 해당 자료구조를 구성하는 구제적인 자료구조가 숨겨질 뿐 아니라 동일한 자료구조의 서로 다른위치에 동시에 접근하기 위해 필요한 개수만큼 이터레이터 객체를 생성함으로써 가능해집니다. 위 구조를 추상 클래스로 가지면 일관된 인터페이스를 통해 다양한 자료구조를 클래스로 정의해서 사용할 수 있습니다.
리스트 클래스는 CreateIterator라는 팩토리메소드를 가지고 있는데 이를 통해 리스트 클래스에서 사용할 Iterator 객체를 생성해서 사용합니다.
class Item { Item(string str) : data(str) {}
private: string data;
};
class AbstractList {
virtual Iterator* createItreator() = 0;
int Count() { return totalCnt; }
virtual void Add(Item* pNew) = 0;
virtual void Remove(Item* pItem) = 0;
virtual void Item* GetItem(int pos) = 0;
protected:
AbstractList() : totalCnt (0) {}
int totalCnt;
};
class LinkedList : public AbstarctList {
LinkedList() : pFirst(0) {}
struct LinkedItem { Item* pData; LinkedItem* pNext; }
Iterator* createIterator { return new ListIterator(this); }
void Add(Item* pNew); { ...; }
void Remove(Item* pItem); { ...; }
void Item* GetItem(int pos); { ...; }
// 연결 리스트에 맞는 함수 재정의
private:
LinkedItem* pFirst;
};
class Iterator {
void First() { curpos = 0; }
void Next() { curpos++; }
vitual Item* GetCurItem() = 0; // 각 자료구조에 맞는 아이템을 찾기위해
protected:
int curpos;
}
class LinkedListIterator {
LinkedListIterator(LinkedList* pList) : m_List(pList);
Item* GetCurItem() { m_List->GetItem(curpos); }
private:
LinkedList* pList;
};
이터레이터 클래스는 객체가 현재 가지고 있는 pos를 직접 접근하는 것이 아니라 생성 시 전달받은 리스트 객체로 필요한 함수를 호출하여 처리하는 구조를 가지고 있습니다.
여기서 이터레이터 클래스는 Client가 항목에 대한 접근을 제어하여 Client가 항목들을 순차적으로 가져와서 원하는 작업을 수행하는 형태인 External Iterator와 Iterator가 자체적으로 각 항목에 순차적으로 접근해서 작업을 수행하는 Internal Iterator로 나뉘어집니다. 둘 중에 External Iterator가 임의로 접근 순서를 제어할 수 있기 때문에 더 유연하고 Internal Iteartor는 함수포인터나 Template Functor 등을 이용해서 정해진 형태의 작업을 수행하는 것이 가능합니다.
이처럼 이터레이터 패턴을 사용하면 여러 개 항목이 모인 자료구조 클래스 객체에 대해 내부 구조를 신경쓰지 않고 각 항목에 접근하여 사용하는 것이 용이해지게 됩니다.
'Programming > C++' 카테고리의 다른 글
C++ 메멘토 패턴 (0) | 2021.06.10 |
---|---|
C++ 중재자 패턴 (0) | 2021.06.06 |
C++ 인터프리터 패턴 (0) | 2021.06.06 |
C++ 커맨드 패턴 (0) | 2021.05.30 |
C++ 책임 연쇄 패턴 (0) | 2021.05.30 |