270 likes | 409 Views
Iterator behavioural design pattern. Eivind J. Nordby University of Karlstad Inst. for Information Technology Dept. of Computer Science Last revised 051010. Introduction. An iterator can be used in order to
E N D
Iterator behavioural design pattern Eivind J. Nordby University of Karlstad Inst. for Information Technology Dept. of Computer Science Last revised 051010
Introduction An iterator can be used in order to • provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation • always used with ”containers” or ”collections” Computer Science, University of Karlstad
The basic idea ”on” or “over” aContainerIterator +first() +next() +isDone() +current() aContainer first() -Element* current anElement anElement current() anElement anElement next() anElement anElement isDone() Computer Science, University of Karlstad
Basic usage List* pMyList = new List; // populate the list ListIterator *pli = new ListIterator(pMyList); // Implicit or explicit: pli->first();while (!pli->isDone()) { // do something with pli->current(); pli->next(); } delete pli; Computer Science, University of Karlstad
Abstract iterators Computer Science, University of Karlstad
Iterators: Consequences • They support variations in the traversal of an aggregate. • through polymorphic iteration • They simplify the Aggregate interface. • remove the iterator interface from aggregate • More than one traversal can be pending on an aggregate. Computer Science, University of Karlstad
Iterate Through the Positions of a Game Board low … pri … pci … … … … … … … … high … Computer Science, University of Karlstad
Motivations • Why positions and not fields? • Does not assume that there is a field • Use the iterator to populate the board with fields • Use the iterator to print the fields • Why separate for columns and rows? • Allows to express explicitly the intention of the iteration • For instance row-wise printing • Without knowing the implementation • Why a common base class (BoardIterator) • Same protocol for all iterators Computer Science, University of Karlstad
Example Client // <select a game, suppressed> Board* myBoard; myBoard = GameFactory::instance().createBoard(); // print the board row by row by // iterating over the positions of the first column BoardIterator *pci = myBoard->columnIterator(); while (!pci->isDone()) { // do something at pci->current(), see next slide pci->next(); // go on to the next game row } delete pci; Computer Science, University of Karlstad
do something with pci->current() // iterate three times over the current row positions const int drawRows = 3; // for each position for (int i = 1; i <= drawRows; i++) { BoardIterator *pri = myBoard->rowIterator(pci->current()); while (!pri->isDone()) { out << myBoard->fieldAt(pri->current())->toString(i); pri->next(); } delete pri; } • OO style: create a new iterator object whenever needed rather than reusing the same one • Simplifies logic and improves correctness Computer Science, University of Karlstad
Graphic alternative • For a graphic client void paint(GraphicContext* gc){ Board* myBoard; myBoard = GameFactory::instance().createBoard(); myBoard->paint(gc); // iterating over the positions of the board BoardIterator *pbi = myBoard->defaultIterator(); while (!pbi->isDone()) { myBoard->fieldAt(pbi->current())->paint(gc); pbi->next(); } delete pci; } // paint Computer Science, University of Karlstad
A more realistic graphic way • In the previous example, each field needs to compute its own position on the graphic surface. • Normally, the graphic picture will consist of a lot of small views, one for each field • The board view paints the board frame • Each field view paints its field and piece • The paiting is inisialized from the operating system • No iterator is needed at all Computer Science, University of Karlstad
A more realistic graphic client class BoardView{ Board* board; // assigned on creation void paint() {board->paint(this->graphicContext();} } // class BoarView class FieldView{ Field* field; void paint() { gc->foregroundColor(fgcOf(field->position()); gc->backgroundColor(bgcOf(field-’>position()); gc->inset(Point(1,1)); field->paint(this->graphicContext(); } Computer Science, University of Karlstad
A graphics aware model class Field{ Piece* piece; void paint(GraphicContext* gc){ if (piece!=0) piece->paint(gc); gc->drawSquare(Point(0,0), gc->size()); } } // class Field class Piece{ Bitmap bitMap; // initialized at creation void paint(GraphicContext* gc){gc->draw(bitMap);} } Computer Science, University of Karlstad
ColumnIterator • Iterate over the positions of a column • Defaults to the first column on the board • ColumnIterator(Board* board); • Alternative constructor for explicit start position • Iterate over a column starting from a given position • ColumnIterator(Board* board, Point position); Computer Science, University of Karlstad
ColumnIterator Features • Constructors • ColumnIterator(Board* board) // Pre: board not null • ColumnIterator(Board* b, Point pos) // Pre: b not null • Accessors • Point current() // Post: result = current position • bool isDone() // Post: result = current not inside board • Transformers • void next() // Post: current stepped to next position Computer Science, University of Karlstad
Abstract BoardIterator • RowIterator and ColumnIterator share • the same protocol • the same constructors • the same implementation • except for the function next() • They are obvious candidates to share • a common (abstract/default) base class • the derived classes (re)define next() • Some ideas follow Computer Science, University of Karlstad
Conceptual Model Computer Science, University of Karlstad
A Possible Implementation • Assume the following • Access methods for Board: • Field* fieldAt(Point position); • Point low(); // lowest position • Point high(); // highest position • Point size(); // dimensions • bool contains(Point position); • Access methods for Field: • Point position(); • Board *board(); Computer Science, University of Karlstad
Design model Computer Science, University of Karlstad
class BoardIterator • Private instance variables Point _current; // the current pos Board* _pBoard; // != null • Implementation invariant • _current.x() is fixed, identifies the column • _current.y() is the current row number • *_pBoard is the board iterated over • Constructor(Board *board) // board != null BoardIterator(Board *board) { _current = board->low(); _pBoard = board} Computer Science, University of Karlstad
class BoardIterator (cont’d) • Public iterator access functions // void first() {_current.setY(board->low().y());} void next() {<move to next in some default order>} boolean isDone() {return !inside(_current);} Point current() {return _current;} private: bool inside(Point position){ return board->contains(position); } • None of the operations need a precondition • Precondition !isDone() only needed when actually accessing a position on a board • Responsibility may be delegated to the client Computer Science, University of Karlstad
class ColumnIterator • Inherits BoardIterator • Implementation invariant • current().x() fixed, identifies the column • current().y() is the current row number • Constructor(Board *board) // board != null ColumnIterator(Board *board) : BoardIterator(board) {} • Redefined operation void next() {current().incY();} Computer Science, University of Karlstad
RowIterator • Very similar to ColumnIterator • same instance variables as ColumnIterator • iterating over the positions of a row, not those of a column • In this example I show another constructor • Taking a specific position as an argument • Iterate over a row starting from the position Computer Science, University of Karlstad
class RowIterator • Public constructors BoardIterator(Board *pb, Point start) { _current = start; _pBoard = pb; } ... RowIterator(Board *pb, Point start) : BoardIterator(pb, start) {} • Redefined access function • void next() {current().incX();} Computer Science, University of Karlstad