프로그래밍 관련/디자인 패턴

[행동 패턴] 반복자 패턴 - Iterator

QA Engineer - P군 2017. 1. 5. 12:02
반응형

[행동 패턴] 반복자 패턴 - Iterator




반복자 패턴은 리스트와 같은 집합 객체들이 내부 표현부를 노출하지 않고 어떤 집합 객체에 속한 원소들을 순차적으로 접근하는 패턴입니다. 


(C++ 에서는 기본적으로 STL 컨테이너를 순회하는 iterator를 기본으로 제공합니다.)



목적


내부 표현부를 노출하지 않고 어떤 집합 객체의 속한 원소들을 순차적으로 접근할 수 있는 방법을 제공합니다.


활용


- 정의한 방법과 다른 방법으로 원소들을 순회하고자 할 때

- 동일한 리스트에 대해서 하나 이상의 순회방법을 정의하고 싶을 때

- 객체 내부 표현 방식을 모르고도 집합 객체의 원소에 접근하고 싶을 때

- 집합 객체를 순회하는 당양한 방법을 지원하고 싶을 때

- 서로 다른 집합 객체 구조에 대해서도 동일한 방법으로 순회하고 싶을 때


장점


- 집합 객체의 다양한 순회 방법을 제공합니다.

- 집합 객체에 따라 하나 이상의 순회 방법이 제공됩니다.


단점


- 순회 알고리즘 구현 부분에 따라서 캡슐화 전략을 위배할 수 도 있습니다.

(구현 부분에 주의가 필요합니다.)

- 사용자가 직접 반복자를 삭제하는 책임을 가져야 합니다.

(힙 메모리를 삭제하지 않아 오류가 발생할 수 있습니다.)



구현


리스트 객체에 접근해서 새로운 내용을 삽입 / 삭제하거나 순회하는 내용을 반복자 객체에 정의하는 것으로 구현될 수 있습니다.


우선 List 클래스를 구현합니다.


1
2
3
4
5
6
7
8
9
10
11
template<class Item>
class List 
{
public :
 
    List(long size = DEFAULT_LIST_CAPACITY);
 
    long Count();
    Item& Get(long index) const;
    //.... 이상의 자세 구현은 책의 부록이므로 삭제..../
};
cs


Iterator의 인터페이스를 구현합니다.


1
2
3
4
5
6
7
8
9
10
11
12
template<class Item>
class Iterator
{
public :
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() const = 0;
    virtual Item CurrentItem() const = 0;
 
protected :
    Iterator();
};
cs


Iterator의 서브클래스를 구현합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//-- 정방향 반복자
template<class Item>
class ListIterator : public Iterator<Item>
{
public:
    ListIterator(const List<Item>* aList);
    virtual void First() override;
    virtual void Next() override;
    virtual bool IsDone() const override;
    virtual Item CurrentItem() const override;
 
private :
 
    const List<Item>* _list;
    long _current;
 
};
 
template<class Item>
ListIterator<Item>::ListIterator (const List<Item>* aList)
    : _list(aList) , _current(0) {}
 
template<class Item>
void ListIterator<Item>::First()
{
    _current = 0;
}
 
template<class Item>
void ListIterator<Item>::Next()
{
    _current++;
}
 
template<class Item>
bool ListIterator<Item>::IsDone() const
{
    return _current >= _list->Count();
}
 
template<class Item>
Item ListIterator<Item>::CurrentItem() const
{
    if (IsDone())
    {
        throw IteratorOutBounds;
    }
 
    return _list->Get(_current);
}
 
///--- 역방향 반복자
template<class Item>
class ReverseListIterator : public Iterator<Item>
{
public:
    ReverseListIterator(const List<Item>* aList);
    virtual void First() override;
    virtual void Next() override;
    virtual bool IsDone() const override;
    virtual Item CurrentItem() const override;
 
private:
 
    const List<Item>* _list;
    long _current;
 
};
 
template<class Item>
ReverseListIterator<Item>::ReverseListIterator(const List<Item>* aList)
{
    _list = aList;
    _current = _list->Count();
}
 
template<class Item>
void ReverseListIterator<Item>::First()
{
    _current = _list->Count();
}
 
template<class Item>
void ReverseListIterator<Item>::Next()
{
    _current--;
}
 
template<class Item>
bool ReverseListIterator<Item>::IsDone() const
{
    return _current <= 0;
}
 
template<class Item>
Item ReverseListIterator<Item>::CurrentItem() const
{
    if (IsDone())
    {
        throw IteratorOutBounds;
    }
 
    return _list->Get(_current);
}
 
cs


이후 List를 제작하고 Iterator를 연산의 인자로 출력해봅니다.


1
2
3
4
5
6
7
void PrintEmployees(Iterator<Employee*>& _data)
{
    for (_data.First(); !_data.IsDone(); _data.Next())
    {
        _data.CurrentItem()->Print();
    }
}
cs



1
2
3
4
5
6
7
8
9
10
11
void main()
{
    List<Employee*>* employees;
 
    ListIterator<Employee*> foward(employees);
    ReverseListIterator<Employee*> backward(employees);
    
    PrintEmployees(foward);
    cout << "----------" << endl;
    PrintEmployees(backward);
}
cs


[결과]



이제 구현 표준을 벗어나는 상황을 방지하면서, List를 변형해봅니다.

바람직한 방법으로는 List 인터페이스를 표준화한 abstrctList 클래스를 만드는 것이 바람직합니다. 그리고 난후에 다양한 구현을 abstrctList 의 서브클래스로 만듭니다.


1
2
3
4
5
6
7
template <class Item>
class AbstractList
{
public :
    virtual Iterator<Item*> CreateIterator() const = 0;
    //..
};
cs



[후기]


책에 나와 있지도 않고 누락된 코드는 작성자가 직접 작성해야 했습니다.ㅠ_ㅠ..

혹시나 책을 사시는 분들은 참고해주세요..


[참고] - GOF의 디자인 패턴


반응형