• No results found

Iterator Pattern. CSIE Department, NTUT Chien-Hung Liu. Provide a way to access the elements of an aggregate object sequentially

N/A
N/A
Protected

Academic year: 2021

Share "Iterator Pattern. CSIE Department, NTUT Chien-Hung Liu. Provide a way to access the elements of an aggregate object sequentially"

Copied!
18
0
0

Loading.... (view fulltext now)

Full text

(1)

Iterator Pattern

CSIE Department, NTUT

Chien-Hung Liu

Iterator: Intent

z

Provide a way to

access

the

elements of an

aggregate

object

sequentially

z

without exposing its underlying

representation

(2)

Iterator: Motivation (1)

z

An aggregate object such as a

list

z

should give you a way to

access its elements

without exposing its internal structure

z

might want to

traverse the list in different ways

,

depending on what you want to accomplish

z

don't want to bloat the List interface with operations for

different traversals

z

might also need to

have more than one traversal

pending on the same list

Iterator: Motivation (1-1)

z

C++ STL Example

list<int> l;

// vector<int> l;

l.push_back(1);

l.push_back(2);

list<int>::reverse_iterator ri;

// vector<int> …

list<int>::iterator i;

// vector <int>::iterator i

for (i = l.begin(); i != l.end(); i++)

(3)

z

The key idea is to take the responsibility

for access and traversal out of the list

object and put it into an iterator

object

Iterator: Motivation (2)

copyright® by Design Patterns: Elements of Reusable Object-Oriented Software, Addison Wesley, 1995

Iterator: Motivation (3)

z

Separating

the traversal mechanism from the List

object

z

lets us define iterators for

different traversal policies

without enumerating them in the List interface.

z

E.g., FilteringListIterator

z

Notice that the client must know that it is a

list

that's

traversed

z

the client

commits to a particular aggregate structure

z

It would be better if we could change the aggregate

class without changing client code

z

by generalizing the iterator concept to support

(4)

Iterator: Motivation (4)

z

With

AbstractList

and

abstract

iterator

, iteration mechanism

becomes independent of concrete aggregate classes

Abstract List

Abstract Iterator

copyright® by Design Patterns: Elements of Reusable Object-Oriented Software, Addison Wesley, 1995

Iterator: Motivation (5)

z

How to create the iterator?

z

How to write code that's independent of the concrete List

subclasses?

z

Make

the list objects

responsible for

creating their

corresponding iterator

z

requires an operation like

CreateIterator

through which

clients request an iterator object

z

CreateIterator is an example of a

factory method

z

The Factory Method approach give rise to two class

(5)

Iterator: Applicability

z

To

access an aggregate object's

contents

without exposing its internal representation

z

To

support multiple traversals

of aggregate

objects

z

To provide a

uniform interface

for

traversing different aggregate structures

z

That is, to

support polymorphic iteration

Iterator: Structure

(6)

Iterator: Participants

z

Iterator

z

defines an interface for accessing and traversing elements.

z

ConcreteIterator

z

implements the Iterator interface.

z

keeps track of the current position

in the traversal of the

aggregate.

z

Aggregate

z

defines an interface for creating an Iterator object.

z

ConcreteAggregate

z

implements the Iterator creation interface

to return an

instance of the proper

ConcreteIterator

.

Iterator: Collaboration

z

A

ConcreteIterator

keeps track of the current

object in the aggregate and can compute the

succeeding object in the traversal.

(7)

Iterator: Consequences

z

It supports variations in the traversal of an aggregate

z

E.g., Code generation may traverse the parse tree inorder or

preorder. Iterators make it easy to change the traversal

algorithm

z

You can also define Iterator subclasses to support new

traversals

z

Iterators simplify the Aggregate interface

z

Iterator's traversal interface obviates the need for a similar

interface in Aggregate

z

More than one traversal can be pending on an

aggregate

z

An iterator keeps track of its own traversal state

Iterator: Implementation (1)

z

Iterator has many implementation variants and

alternatives. The trade-offs often depend on the

control structures your language provides.

z

Who controls the iteration?

z

External iterator

(or active iterators)

z

when the client controls the iteration

z

clients that use an external iterator must advance the

traversal and request the next element explicitly from the

iterator

z

more flexible than internal iterators

z

easy to compare two collections for equality

(8)

Iterator: Implementation (2)

z

Internal iterator

(or

passive

iterators)

z

the iterator controls the iteration

z

the client hands an internal iterator an operation to

perform, and the iterator applies that operation to

every element in the aggregate

z

easier to use, because they define the iteration logic

for you

Iterator: Implementation (3)

z

Who defines the traversal algorithm?

(1) The iterator is responsible to define the traversal algorithm

z

easy to use different iteration algorithms on the same

aggregate

z

also be easier to reuse the same algorithm on different

aggregates

(2) Aggregate define the traversal algorithm and use the

iterator to store just the state of the iteration

z

this kind of iterator is called a

cursor

since it merely points to

the current position in the aggregate

z

the traversal algorithm can access the private variables of the

(9)

Iterator: Implementation (4)

z

How robust is the iterator?

z

A

robust iterator

z

ensures that insertions and removals won't interfere

with traversal

z

it does traversal without copying the aggregate

z

Many ways to implement robust iterators.

z

Most rely on

registering

the iterator with the

aggregate

z

On insertion or removal

ƒ

the aggregate either adjusts the internal state of iterators, or

ƒ

it maintains information internally to ensure proper traversal

Iterator: Implementation (4-1)

z

How robust is the iterator?

z

C++ STL exampe

for (i = l.begin(); i != l.end(); i++)

if (*i == 5) {

l.erase(i);

break;

}

break is necessary

(i.e., the loop can not

continue)

l.remove(5);

You do not need a loop to remove

an element from a list

(10)

Iterator: Implementation (5)

z

Additional Iterator operations

z

The minimal interface to Iterator consists of the

operations

First

,

Next

,

IsDone

, and

CurrentItem

z

Some additional operations might prove useful.

z

E.g., ordered aggregates can have a

Previous

operation that positions the iterator to the previous

element.

z

A

SkipTo

operation is useful for sorted or indexed

collections.

ƒ

SkipTo positions the iterator to an object matching specific

criteria

Iterator: Implementation (6)

z

Using polymorphic iterators in C++

z

Polymorphic iterators have their cost

z

require the iterator object to be allocated dynamically by a

factory method

z

should be used only when there's a need for polymorphism

ƒ

Otherwise use concrete iterators (allocated on the stack)

z

Another drawback: the client is responsible for deleting them

z

We can use a stack-allocated proxy (i.e., Proxy pattern) as a

stand-in for the real iterator. (

resource allocation is initialization

)

ƒ

The proxy deletes the iterator in its destructor. Thus when the

proxy goes out of scope, the real iterator will get deallocated along

with it

(11)

Iterator: Implementation (7)

z

Iterators may have privileged access

z

An iterator can be viewed as an extension of the aggregate

that created it

z

The iterator and the aggregate are tightly coupled

z

We can express this close relationship in C++ by making

the iterator a

friend

of its aggregate

Iterator: Implementation (8)

z

Iterators for composites

z

External iterators can be difficult to implement

over recursive

aggregate structures like those in the Composite pattern

z

because a position in the structure may span many levels of nested

aggregates

z

has to store a path through the Composite to keep track of the current

object

z

Sometimes

it's easier just to use an internal iterator

z

It can record the current position simply by calling itself recursively,

thereby storing the path implicitly in the call stack

z

If the nodes in a Composite have an interface for moving from a

node to its

siblings

,

parents

, and

children

, then a

cursor-based

iterator

may offer a better alternative.

z

Composites often need to be traversed in more than one way.

z

E.g., Preorder, postorder, inorder, and breadth-first traversals

z

support each kind of traversal with a different class of iterator

(12)

Iterator: Implementation (9)

z

Null iterators

z

A NullIterator

is a degenerate iterator that's helpful for

handling boundary conditions

z

By definition, a NullIterator is

always

done with traversal;

that is, its

IsDone operation always evaluates to true

z

NullIterator can make traversing tree-structured aggregates

(like Composites) easier.

z

That lets us implement traversal over the entire structure in

a uniform way

z

At each point in the traversal, we ask the current element

for an iterator for its children. Aggregate elements return a

concrete iterator as usual. But leaf elements return an

instance of NullIterator.

Iterator: Sample Code (1)

template <class Item>

class List {

public:

List(long size = DEFAULT_LIST_CAPACITY);

long Count() const;

Item& Get(long index) const;

// ...

};

template <class Item>

class Iterator {

public:

virtual void First() = 0;

virtual void Next() = 0;

virtual bool IsDone() const = 0;

virtual Item CurrentItem() const = 0;

(13)

Iterator: Sample Code (2)

template <class Item>

class ListIterator : public Iterator<Item> { public:

ListIterator(const List<Item>* aList); virtual void First();

virtual void Next();

virtual bool IsDone() const; virtual Item CurrentItem() const; private:

const List<Item>* _list; long _current;

};

Iterator subclass implementations

Iterator: Sample Code (3)

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 IteratorOutOfBounds; } return _list->Get(_current); }

First

Next

IsDone

CurrentItem

implementation

(14)

Iterator: Sample Code (4)

void PrintEmployees (Iterator<Employee*>& i) { for (i.First(); !i.IsDone(); i.Next()) {

i.CurrentItem()->Print(); }

}

Using the iterators

List<Employee*>* employees; // ... ListIterator<Employee*> forward(employees); ReverseListIterator<Employee*> backward(employees); PrintEmployees(forward); PrintEmployees(backward);

Iterator: Sample Code (5)

SkipList<Employee*>* employees; // ...

SkipListIterator<Employee*> iterator(employees); PrintEmployees(iterator);

Avoiding commitment to a specific list implementation

template <class Item>

class AbstractList { public:

virtual Iterator<Item>* CreateIterator() const = 0; // ...

};

template <class Item>

Iterator<Item>* List<Item>::CreateIterator () const { return new ListIterator<Item>(this);

}

// we know only that we have an AbstractList AbstractList<Employee*>* employees;

// ...

(15)

Iterator: Sample Code (6)

template <class Item> class IteratorPtr { public:

IteratorPtr(Iterator<Item>* i): _i(i) { } ~IteratorPtr() { delete _i; }

Iterator<Item>* operator->() { return _i; } Iterator<Item>& operator*() { return *_i; } private:

// disallow copy and assignment to avoid // multiple deletions of _i:

IteratorPtr(const IteratorPtr&);

IteratorPtr& operator=(const IteratorPtr&); private:

Iterator<Item>* _i; };

Making sure iterators get deleted

AbstractList<Employee*>* employees; // ... IteratorPtr<Employee*> iterator(employees->CreateIterator()); PrintEmployees(*iterator);

acts as a proxy

IteratorPtr can

be treated just

like a pointer

to an iterator

Allocated in stack

Iterator: Sample Code (7)

template <class Item> class ListTraverser { public:

ListTraverser(List<Item>* aList);

bool Traverse();

protected:

virtual bool ProcessItem(const Item&) = 0;

private:

ListIterator<Item> _iterator; };

An internal ListIterator

z

How to parameterize the iterator with the operation we want to

perform on each element

z

Pass in a pointer to a function (global or static)

z

the iterator calls the operation passed to it at each iteration

z

Rely on subclassing (of each traversal)

(16)

Iterator: Sample Code (8)

template <class Item>

ListTraverser<Item>::ListTraverser ( List<Item>* aList

) : _iterator(aList) { } template <class Item>

bool ListTraverser<Item>::Traverse () { bool result = false;

for ( _iterator.First(); !_iterator.IsDone(); _iterator.Next() ) { result = ProcessItem(_iterator.CurrentItem()); if (result == false) { break; } } return result; }

Iterator: Sample Code (9)

class PrintNEmployees : public ListTraverser<Employee*> { public:

PrintNEmployees(List<Employee*>* aList, int n) : ListTraverser<Employee*>(aList),

_total(n), _count(0) { } protected:

bool ProcessItem(Employee* const&); private:

int _total; int _count; };

bool PrintNEmployees::ProcessItem (Employee* const& e) { _count++;

e->Print();

return _count < _total; }

List<Employee*>* employees; // ...

(17)

Iterator: Sample Code (10)

ListIterator<Employee*> i(employees); int count = 0;

for (i.First(); !i.IsDone(); i.Next()) { count++; i.CurrentItem()->Print(); if (count >= 10) { break; } }

template <class Item>

class FilteringListTraverser { public:

FilteringListTraverser(List<Item>* aList); bool Traverse();

protected:

virtual bool ProcessItem(const Item&) = 0;

virtual bool TestItem(const Item&) = 0;

private:

ListIterator<Item> _iterator; };

Using External Iterator

Internal iterators can encapsulate different kinds of iteration

Iterator: Sample Code (11)

template <class Item>

void FilteringListTraverser<Item>::Traverse () { bool result = false;

for ( _iterator.First(); !_iterator.IsDone(); _iterator.Next() ) { if (TestItem(_iterator.CurrentItem())) { result = ProcessItem(_iterator.CurrentItem()); if (result == false) { break; } } } return result; } Subclasses override TestItem to specify the test

(18)

Iterator: Related patterns

z

Composite

z

Iterators are often applied to recursive structures such as

Composites

z

Factory Method

z

Polymorphic iterators

rely on factory methods to instantiate

the appropriate Iterator

z

Memento

z

is often used in conjunction with the Iterator pattern.

z

An iterator can use a memento to capture the state of an

References

Related documents

• reverse (bool) – whether the iterator should iterate in reverse order • start (bytes) – the start key (inclusive by default) of the iterator range • stop (bytes) – the stop

bool priority queue:: empty () const ; C ontainer::size type. priority queue:: size () const

Since Set interface or HashSet class doesn't provide will get method to retrieve elements the gift way your take out elements from schedule Set group to iterate over population by

The java iterator, implement serializable interface adds them need of implementing efficient traversal of linked list collection to iterate through elements using.. This

The forget items received no retrieval practice opportunities in any of the retrieval practice schedules, but might be expected to undergo greater forgetting in the maximize

Early iterations were related to training data selection, activation functions, and what type of neural network should be used: MLP, LSTM, GRU, or CNN.. The

This paper discusses the development of several Economic Model Predictive Control (EMPC) based strategies for solving an energy dispatch problem in a smart micro-grid.. The smart

These examples show how you can find elements from practically any sector, analyze them for similarities and differences and then write a joke bringing these two facets together.