// Purpose. Iterator design pattern // Take traversal-of-a-collection functionality out of the collection and // promote it to "full object status". This simplifies the collection, allows // many traversals to be active simultaneously, and decouples collection algo- // rithms from collection data structures. // "Every 'container-like' class must have an iterator." [Lakos] It may seem // like a violation of encapsulation for a Stack class to allow its users to // access its contents directly, but John Lakos' argument is that the designer // of a class inevitably leaves something out. Later, when users need addi- // tional functionality, if an iterator was originally provided, they can add // functionality with "open for extension, closed for modification". Without // an iterator, their only recourse is to invasively customize production // code. Below, the orginal Stack class did not include an equality operator, // but it did include an iterator. As a result, the equality operator could // be readily retrofitted. // 1. Design an "iterator" class for the "container" class // 2. Add a createIterator() member to the container class // 3. Clients ask the container object to create an iterator object // 4. Clients use the first(), isDone(), next(), and currentItem() protocol #include using namespace std; class Stack { int items[10]; int sp; public: friend class StackIter; Stack() { sp = -1; } void push( int in ) { items[++sp] = in; } int pop() { return items[sp--]; } bool isEmpty() { return (sp == -1); } StackIter* createIterator() const; // 2. Add a createIterator() member }; class StackIter { // 1. Design an "iterator" class const Stack* stk; int index; public: StackIter( const Stack* s ) { stk = s; } void first() { index = 0; } void next() { index++; } bool isDone() { return index == stk->sp+1; } int currentItem() { return stk->items[index]; } }; StackIter* Stack::createIterator() const { return new StackIter( this ); } bool operator==( const Stack& l, const Stack& r ) { // 3. Clients ask the container object to create an iterator object StackIter* itl = l.createIterator(); StackIter* itr = r.createIterator(); // 4. Clients use the first(), isDone(), next(), and currentItem() protocol for (itl->first(), itr->first(); ! itl->isDone(); itl->next(), itr->next()) if (itl->currentItem() != itr->currentItem()) break; bool ans = itl->isDone() && itr->isDone(); delete itl; delete itr; return ans; } void main( void ) { Stack s1; for (int i=1; i < 5; i++) s1.push(i); Stack s2( s1 ), s3( s1 ), s4( s1 ), s5( s1 ); s3.pop(); s5.pop(); s4.push( 2 ); s5.push( 9 ); cout << "1 == 2 is "<< (s1 == s2) << endl; cout << "1 == 3 is "<< (s1 == s3) << endl; cout << "1 == 4 is "<< (s1 == s4) << endl; cout << "1 == 5 is "<< (s1 == s5) << endl; } // 1 == 2 is 1 // 1 == 3 is 0 // 1 == 4 is 0 // 1 == 5 is 0 // Purpose. Iterator using operators instead of methods // // Discussion. John Lakos suggests the GOF and STL iterator interfaces are: // error-prone (possibility of misspelling method names), clumsy, and require // too much typing. This design uses nothing but "intuitive" operators. // Notice also that no createIterator() was specified. The user creates these // iterators as local variables, and no clean-up is necessary. #include using namespace std; class Stack { int items[10]; int sp; public: friend class StackIter; Stack() { sp = -1; } void push( int in ) { items[++sp] = in; } int pop() { return items[sp--]; } bool isEmpty() { return (sp == -1); } }; class StackIter { const Stack& stk; int index; public: StackIter( const Stack& s ) : stk( s ) { index = 0; } void operator++() { index++; } bool operator()() { return index != stk.sp+1; } int operator*() { return stk.items[index]; } }; bool operator==( const Stack& l, const Stack& r ) { StackIter itl( l ), itr( r ); for ( ; itl(); ++itl, ++itr) if (*itl != *itr) break; return ! itl() && ! itr(); } void main( void ) { Stack s1; int i; for (i=1; i < 5; i++) s1.push(i); Stack s2( s1 ), s3( s1 ), s4( s1 ), s5( s1 ); s3.pop(); s5.pop(); s4.push( 2 ); s5.push( 9 ); cout << "1 == 2 is "<< (s1 == s2) << endl; cout << "1 == 3 is "<< (s1 == s3) << endl; cout << "1 == 4 is "<< (s1 == s4) << endl; cout << "1 == 5 is "<< (s1 == s5) << endl; } // 1 == 2 is 1 // 1 == 3 is 0 // 1 == 4 is 0 // 1 == 5 is 0 // Purpose. Iterator using the Java interface // // Discussion. Java's standard collection classes have a much leaner inter- // face. Their next() methods combine the next() and currentItem() methods. #include using namespace std; class Stack { int items[10]; int sp; public: friend class StackIter; Stack() { sp = -1; } void push( int in ) { items[++sp] = in; } int pop() { return items[sp--]; } bool isEmpty() { return (sp == -1); } StackIter* iterator() const; }; class StackIter { const Stack* stk; int index; public: StackIter( const Stack* s ) { stk = s; index = 0; } int next() { return stk->items[index++]; } bool hasNext() { return index != stk->sp+1; } }; StackIter* Stack::iterator() const { return new StackIter( this ); } bool operator==( const Stack& l, const Stack& r ) { StackIter* itl = l.iterator(); StackIter* itr = r.iterator(); while (itl->hasNext()) if (itl->next() != itr->next()) { delete itl; delete itr; return false; } bool ans = (! itl->hasNext()) && ( ! itr->hasNext()); delete itl; delete itr; return ans; } void main( void ) { Stack s1; int i; for (i=1; i < 5; i++) s1.push(i); Stack s2( s1 ), s3( s1 ), s4( s1 ), s5( s1 ); s3.pop(); s5.pop(); s4.push( 2 ); s5.push( 9 ); cout << "1 == 2 is "<< (s1 == s2) << endl; cout << "1 == 3 is "<< (s1 == s3) << endl; cout << "1 == 4 is "<< (s1 == s4) << endl; cout << "1 == 5 is "<< (s1 == s5) << endl; } // 1 == 2 is 1 // 1 == 3 is 0 // 1 == 4 is 0 // 1 == 5 is 0 // Purpose. Simple implementation of an Iterator (uses parallel dynamic array) // // Discussion. Instead of having to write an involved stack-like solution to // handle step-wise recursive descent, use a little extra memory to maintain a // sequential representation of the original hierarchical data. The replicated // data are not Node objects, they are lightweight pointers. The array is // initialized using a recursive method similar to traverse(Node*). #include #include using namespace std; class BST { friend class Iterator; struct Node { Node( int ); int value; Node* left; Node* rite; }; Node* root; int size; void add( Node**, int ); void traverse( Node* ); public: BST() { root = 0; size = 0; } void add( int ); void traverse(); Iterator* createIterator() const; }; class Iterator { const BST* tree; BST::Node** array; int index; void populateArray( BST::Node* current ) { if (current->left) populateArray( current->left ); array[index++] = current; if (current->rite) populateArray( current->rite ); } public: Iterator( const BST* s ) { tree = s; array = 0; index = 0; } ~Iterator() { delete [] array; } void first() { delete [] array; array = new BST::Node*[tree->size]; index = 0; populateArray( tree->root ); index = 0; } void next() { index++; } bool isDone() { return index == tree->size; } BST::Node* currentItem() { return array[index]; } }; void main( void ) { srand( time( 0 ) ); BST tree; for (int i=0; i < 20; i++) tree.add( rand() % 20 + 1 ); cout << "traverse: "; tree.traverse(); cout << "\niterator: "; Iterator* it = tree.createIterator(); for (it->first(); ! it->isDone(); it->next()) cout << it->currentItem()->value << ' '; cout << "\niterator: "; for (it->first(); ! it->isDone(); it->next()) cout << it->currentItem()->value << ' '; cout << '\n'; } // traverse: 1 2 3 7 8 9 9 10 11 11 13 14 14 14 15 17 18 19 19 20 // iterator: 1 2 3 7 8 9 9 10 11 11 13 14 14 14 15 17 18 19 19 20 // iterator: 1 2 3 7 8 9 9 10 11 11 13 14 14 14 15 17 18 19 19 20 BST::Node::Node( int v ) { value = v; left = rite = 0; } void BST::add( Node** n, int v ) { if ( ! *n) { *n = new Node( v ); size++; } else if (v <= (*n)->value) add( &((*n)->left), v ); else add( &((*n)->rite), v ); } void BST::add( int v ) { add( &root, v ); } void BST::traverse() { traverse( root ); } void BST::traverse( Node* n ) { if (n->left) traverse( n->left ); cout << n->value << ' '; if (n->rite) traverse( n->rite ); } Iterator* BST::createIterator() const { return new Iterator( this ); } // Purpose. Fairly reusable iterator for step-wise recursive descent // // Discussion. The Composite/Component/Leaf code is one of the previous // Composite demos. Methods added were: Component::outputValue() and // Composite::createIterator(). #include #include using namespace std; class Component { public: virtual void traverse() = 0; virtual void outputValue() { } }; class Leaf : public Component { int value; public: Leaf( int val ) { value = val; } /*virtual*/ void traverse() { cout << value << ' '; } /*virtual*/ void outputValue() { traverse(); } }; class Composite : public Component { vector children; public: friend class Iterator; void add( Component* ele ) { children.push_back( ele ); } /*virtual*/ void traverse() { for (int i=0; i < children.size(); i++) children[i]->traverse(); } Iterator* createIterator(); }; class Memento { public: struct MgrIdx { MgrIdx( Composite* m=0, int i=0 ) { mgr = m; idx = i; } Composite* mgr; int idx; }; void initialize( Composite* root ) { vec.resize( 0 ); vec.push_back( MgrIdx( root, 0 ) ); } bool isEmpty() { return vec.size() == 0; } void push( MgrIdx ds ) { vec.push_back( ds ); } MgrIdx top() { return vec.back(); } MgrIdx pop() { MgrIdx ds = vec.back(); vec.pop_back(); return ds; } private: vector vec; }; // Dependencies on actual class playing the role of "Composite": // Composite class name, children attribute name class Iterator { Composite* root; Memento memento; bool done; public: Iterator( Composite* rooot ) { root = rooot; } void first() { memento.initialize( root ); done = false; } void next() { Memento::MgrIdx ds = memento.pop(); ds.idx++; // if (and while) you are at end-of-composite, go up while (ds.idx == ds.mgr->children.size()) { if (memento.isEmpty()) { done = true; return; } ds = memento.pop(); ds.idx++; } memento.push( ds ); Composite* compo; if (compo = dynamic_cast(ds.mgr->children[ds.idx])) memento.push( Memento::MgrIdx( compo, 0 ) ); } Component* currentItem() { Memento::MgrIdx ds = memento.top(); return ds.mgr->children[ds.idx]; } bool isDone() { return done; } }; Iterator* Composite::createIterator() { return new Iterator( this ); } void main( void ) { Composite containers[4]; for (int i=0; i < 4; i++) for (int j=0; j < 3; j++) containers[i].add( new Leaf( i * 3 + j ) ); for (i=1; i < 4; i++) containers[0].add( &(containers[i]) ); cout << "traverse: "; containers[0].traverse(); Iterator* it = containers[0].createIterator(); cout << "\niterator: "; for (it->first(); ! it->isDone(); it->next()) it->currentItem()->outputValue(); cout << '\n'; cout << "iterator: "; for (it->first(); ! it->isDone(); it->next()) it->currentItem()->outputValue(); cout << '\n'; delete it; } // traverse: 0 1 2 3 4 5 6 7 8 9 10 11 // iterator: 0 1 2 3 4 5 6 7 8 9 10 11 // iterator: 0 1 2 3 4 5 6 7 8 9 10 11