Iterator — поведенческийшаблон проектирования. Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из агрегированных объектов.
Перебор элементов выполняется объектом итератора, а не самой коллекцией. Это упрощает интерфейс и реализацию коллекции, а также способствует более логичному разделению обязанностей.
Особенностью полноценно реализованного итератора является то, что код, использующий итератор, может ничего не знать о типе итерируемого агрегата.
Конечно же, (в C++) почти в любом агрегате можно выполнять итерации указателем void*, но при этом:
не ясно, что является значением «конец агрегата», для двусвязного списка это &ListHead, для массива это &array[size], для односвязного списка это NULL
операция Next сильно зависит от типа агрегата.
Итераторы позволяют абстрагироваться от типа и признака окончания агрегата, используя полиморфный Next (часто реализованный как operator++ в C++) и полиморфный aggregate.end(), возвращающий значение «конец агрегата».
Таким образом, появляется возможность работы с диапазонами итераторов, при отсутствии знания о типе итерируемого агрегата. Например:
/*sample code in C#This structural code demonstrates the Iterator pattern which provides for a way to traverse (iterate) over a collection of items without detailing the underlying structure of the collection.*/Hidecode// Iterator pattern -- Structural exampleusingSystem;usingSystem.Collections;namespaceDoFactory.GangOfFour.Iterator.Structural{/// <summary>/// MainApp startup class for Structural/// Iterator Design Pattern./// </summary>classMainApp{/// <summary>/// Entry point into console application./// </summary>staticvoidMain(){ConcreteAggregatea=newConcreteAggregate();a[0]="Item A";a[1]="Item B";a[2]="Item C";a[3]="Item D";// Create Iterator and provide aggregateConcreteIteratori=newConcreteIterator(a);Console.WriteLine("Iterating over collection:");objectitem=i.First();while(!i.IsDone()){Console.WriteLine(item);item=i.Next();}// Wait for userConsole.ReadKey();}}/// <summary>/// The 'Aggregate' abstract class/// </summary>abstractclassAggregate{publicabstractIteratorCreateIterator();publicabstractintCount{get;protectedset;}publicabstractobjectthis[intindex]{get;set;}}/// <summary>/// The 'ConcreteAggregate' class/// </summary>classConcreteAggregate:Aggregate{privatereadonlyArrayList_items=newArrayList();publicoverrideIteratorCreateIterator(){returnnewConcreteIterator(this);}// Gets item countpublicoverrideintCount{get{return_items.Count;}protectedset{}}// Indexerpublicoverrideobjectthis[intindex]{get{return_items[index];}set{_items.Insert(index,value);}}}/// <summary>/// The 'Iterator' abstract class/// </summary>abstractclassIterator{publicabstractobjectFirst();publicabstractobjectNext();publicabstractboolIsDone();publicabstractobjectCurrentItem();}/// <summary>/// The 'ConcreteIterator' class/// </summary>classConcreteIterator:Iterator{privatereadonlyAggregate_aggregate;privateint_current;// ConstructorpublicConcreteIterator(Aggregateaggregate){this._aggregate=aggregate;}// Gets first iteration itempublicoverrideobjectFirst(){return_aggregate[0];}// Gets next iteration itempublicoverrideobjectNext(){objectret=null;_current++;if(_current<_aggregate.Count){ret=_aggregate[_current];}returnret;}// Gets current iteration itempublicoverrideobjectCurrentItem(){return_aggregate[_current];}// Gets whether iterations are completepublicoverrideboolIsDone(){return_current>=_aggregate.Count;}}}OutputIteratingovercollection:ItemAItemBItemCItemD
/** * Паттерн итератор предоставляет механизм последовательного перебора элементов коллекции без раскрытия реализации коллекции. * * Перебор элементов выполняется объектом итератора, а не самой коллекцией. * Это упрощает интерфейс и реализацию коллекции, а также способствует более логичному распределению обязанностей. */namespaceiterator1{/** * Наличие общего интерфейса удобно для клиента, поскольку клиент отделяется от реализации коллекции объектов. * * ConcreteAggregate содержит коллекцию объектов и реализует метод, который возвращает итератор для этой коллекции. */interfaceIAggregate{/** * Каждая разновидность ConcreteAggregate отвечает за создание экземпляра Concrete Iterator, * который может использоваться для перебора своей коллекции объектов. */publicfunctioncreateIterator();}/** * Интерфейс Iterator должен быть реализован всеми итераторами. * * ConcreteIterator отвечает за управление текущей позицией перебора. */interfaceIIterator{/** * @abstract * @return boolean есть ли следующий элемент в коллекции */publicfunctionhasNext();/** * @abstract * @return mixed следующий элемент массива */publicfunctionnext();/** * Удаляет текущий элемент коллекции * @abstract * @return void */publicfunctionremove();}/** * В моём примере обе коллекции используют одинаковый итератор - итератор массива. */classConcreteAggregate1implementsIAggregate{/** * @var Item[] $items */public$items=array();publicfunction__construct(){$this->items=array(newItem(1,2),newItem(1,2),newItem(1,2),);}publicfunctioncreateIterator(){returnnewConcreteIterator1($this->items);}}classConcreteAggregate2implementsIAggregate{/** * @var Item[] $items */public$items=array();publicfunction__construct(){$this->items=array(newItem(2,3),newItem(2,3),newItem(2,3),);}publicfunctioncreateIterator(){returnnewConcreteIterator1($this->items);}}classConcreteIterator1implementsIIterator{/** * @var Item[] $items */protected$items=array();/** * @var int $position хранит текущую позицию перебора в массиве */public$position=0;/** * @param $items массив объектов, для перебора которых создается итератор */publicfunction__construct($items){$this->items=$items;}publicfunctionhasNext(){if($this->position>=count($this->items)||count($this->items)==0){return(false);}else{return(true);}}publicfunctionnext(){$menuItem=$this->items[$this->position];$this->position++;return($menuItem);}publicfunctionremove(){if($this->position<=0){thrownew\Exception('Нельзя вызывать remove до вызова хотя бы одного next()');}if($this->items[$this->position-1]!=null){for($i=$this->position-1;$i<count($this->items);$i++){$this->items[$i]=$this->items[$i+1];}$this->items[count($this->items)-1]=null;}}}classClient{/** * @var ConcreteAggregate1 $aggregate1 */public$aggregate1;/** * @var ConcreteAggregate2 $aggregate2 */public$aggregate2;publicfunction__construct($aggregate1,$aggregate2){$this->aggregate1=$aggregate1;$this->aggregate2=$aggregate2;}publicfunctionprintAggregatesItems(){$iterator1=$this->aggregate1->createIterator();echo"\n First";$this->printIteratorItems($iterator1);$iterator2=$this->aggregate2->createIterator();echo"\n\n Second";$this->printIteratorItems($iterator2);}/** * @param $iterator IIterator */privatefunctionprintIteratorItems($iterator){while($iterator->hasNext()){$item=$iterator->next();echo"\n$item->name$item->price$item->description";}}}classItem{public$price;public$name;public$description;publicfunction__construct($name,$price,$description=''){$this->name=$name;$this->price=$price;$this->description=$description;}}$runner=newClient(newConcreteAggregate1(),newConcreteAggregate2());$runner->printAggregatesItems();}
Пример итератора компоновщика на PHP5
Исходный текст итератора компоновщика на языке PHP5
/** * Паттерн-компоновщик с внешним итератором * Итератор использует рекурсию для перебора дерева элементов */namespacecompositeIterator{/** * Клиент использует интерфейс AComponent для работы с объектами. * Интерфейс AComponent определяет интерфейс для всех компонентов: как комбинаций, так и листовых узлов. * AComponent может реализовать поведение по умолчанию для add() remove() getChild() и других операций */abstractclassAComponent{public$customPropertyName;public$customPropertyDescription;/** * @param AComponent $component */publicfunctionadd($component){thrownew\Exception("Unsupported operation");}/** * @param AComponent $component */publicfunctionremove($component){thrownew\Exception("Unsupported operation");}/** * @param int $int */publicfunctiongetChild($int){thrownew\Exception("Unsupported operation");}/** * @return IPhpLikeIterator */abstractfunctioncreateIterator();publicfunctionoperation1(){thrownew\Exception("Unsupported operation");}}/** * Leaf наследует методы add() remove() getChild( которые могут не иметь смысла для листового узла. * Хотя листовой узер можно считать узлом с нулём дочерних объектов * * Leaf определяет поведение элементов комбинации. Для этого он реализует операции, поддерживаемые интерфейсом Composite. */classLeafextendsAComponent{publicfunction__construct($name,$description=''){$this->customPropertyName=$name;$this->customPropertyDescription=$description;}publicfunctioncreateIterator(){returnnewNullIterator();}publicfunctionoperation1(){echo("\n I'am leaf {$this->customPropertyName}, i don't want to do operation 1. {$this->customPropertyDescription}");}}classNullIteratorimplementsIPhpLikeIterator{publicfunctionvalid(){return(false);}publicfunctionnext(){return(false);}publicfunctioncurrent(){return(null);}publicfunctionremove(){thrownew\CException('unsupported operation');}}/** * Интерфейс Composite определяет поведение компонентов, имеющих дочерние компоненты, и обеспечивает хранение последних. * * Composite также реализует операции, относящиеся к Leaf. Некоторые из них не могут не иметь смысла для комбинаций; в таких случаях генерируется исключение. */classCompositeextendsAComponent{private$_iterator=null;/** * @var \ArrayObject AComponent[] $components для хранения потомков типа AComponent */public$components=null;publicfunction__construct($name,$description=''){$this->customPropertyName=$name;$this->customPropertyDescription=$description;}/** * @param AComponent $component */publicfunctionadd($component){if(is_null($this->components)){$this->components=new\ArrayObject;}$this->components->append($component);}publicfunctionremove($component){foreach($this->componentsas$i=>$c){if($c===$component){unset($this->components[$i]);}}}publicfunctiongetChild($int){return($this->components[$int]);}publicfunctionoperation1(){echo"\n\n$this->customPropertyName$this->customPropertyDescription";echo"\n --------------------------------";$iterator=$this->components->getIterator();while($iterator->valid()){$component=$iterator->current();$component->operation1();$iterator->next();}}/** * @return CompositeIterator */publicfunctioncreateIterator(){if(is_null($this->_iterator)){$this->_iterator=newCompositeIterator($this->components->getIterator());}return($this->_iterator);}}/** * Рекурсивный итератор компоновщика */classCompositeIteratorimplementsIPhpLikeIterator{public$stack=array();/** * @param \ArrayIterator $componentsIterator */publicfunction__construct($componentsIterator){//$this->stack= new \ArrayObject;$this->stack[]=$componentsIterator;}publicfunctionremove(){thrownew\CException('unsupported operation');}publicfunctionvalid(){if(empty($this->stack)){return(false);}else{/** @var $componentsIterator \ArrayIterator */// берём первый элемент$componentsIterator=array_shift(array_values($this->stack));if($componentsIterator->valid()){return(true);}else{array_shift($this->stack);return($this->valid());}}}publicfunctionnext(){/** @var $componentsIterator \ArrayIterator */$componentsIterator=current($this->stack);$component=$componentsIterator->current();if($componentinstanceofComposite){array_push($this->stack,$component->createIterator());}$componentsIterator->next();//return($component);}publicfunctioncurrent(){if($this->valid()){/** @var $componentsIterator \ArrayIterator */// берём первый элемент$componentsIterator=array_shift(array_values($this->stack));return($componentsIterator->current());}else{return(null);}}}/** * Интерфейс Iterator должен быть реализован всеми итераторами. * Данный интерфейс является частью интерфейса стандартного php итератора. * Конкретный Iterator отвечает за управление текущей позицией перебора в конкретной коллекции. */interfaceIPhpLikeIterator{/** * @abstract * @return boolean есть ли текущий элемент */publicfunctionvalid();/** * @abstract * @return mixed перевести курсор дальше */publicfunctionnext();/** * @abstract * @return mixed получить текущий элемент */publicfunctioncurrent();/** * удалить текущий элемент коллекции * @abstract * @return void */publicfunctionremove();}classClient{/** * @var AComponent */public$topItem;publicfunction__construct($topItem){$this->topItem=$topItem;}publicfunctionprintOperation1(){$this->topItem->operation1();}publicfunctionprintOperation2(){echo"\n\n\n";$iterator=$this->topItem->createIterator();while($iterator->valid()){/** @var $component AComponent */$component=$iterator->current();if(strstr($component->customPropertyName,'leaf1')){echo("\n I'm Client, I found leaf {$component->customPropertyName}, I'll just leave it here (for my 'first-leafs' tea collection). {$component->customPropertyDescription}");}$iterator->next();}}}classTest{publicstaticfunctiongo(){$a=newComposite("c1");$b=newComposite("c2");$c=newComposite("c3");$topItem=newComposite("top item");$topItem->add($a);$topItem->add($b);$topItem->add($c);$a->add(newLeaf("c1-leaf1"));$a->add(newLeaf("c1-leaf2"));$b->add(newLeaf("c2-leaf1"));$b->add(newLeaf("c2-leaf2"));$b->add(newLeaf("c2-leaf3"));$c->add(newLeaf("c3-leaf1"));$c->add(newLeaf("c3-leaf2"));$client=newClient($topItem);$client->printOperation1();$client->printOperation2();}}Test::go();}
fromabcimportABCMeta,abstractmethodclassIterator(metaclass=ABCMeta):""" Абстрактный итератор """_error=None# класс ошибки, которая прокидывается в случае выхода за границы коллекцииdef__init__(self,collection,cursor):""" Constructor. :param collection: коллекция, по которой производится проход итератором :param cursor: изначальное положение курсора в коллекции (ключ) """self._collection=collectionself._cursor=cursor@abstractmethoddefcurrent(self):""" Вернуть текущий элемент, на который указывает итератор """pass@abstractmethoddefnext(self):""" Сдвинуть курсор на следующий элемент коллекции и вернуть его """pass@abstractmethoddefhas_next(self):""" Проверить, существует ли следующий элемент коллекции """pass@abstractmethoddefremove(self):""" Удалить текущий элемент коллекции, на который указывает курсор """passdef_raise_key_exception(self):""" Прокинуть ошибку, связанную с невалидным индексом, содержащимся в курсоре """raiseself._error('Collection of class {} does not have key "{}"'.format(self.__class__.__name__,self._cursor))classListIterator(Iterator):""" Итератор, проходящий по обычному списку """_error=IndexErrordef__init__(self,collection:list):super().__init__(collection,0)defcurrent(self):ifself._cursor<len(self._collection):returnself._collection[self._cursor]self._raise_key_exception()defnext(self):iflen(self._collection)>=self._cursor+1:self._cursor+=1returnself._collection[self._cursor]self._raise_key_exception()defhas_next(self):returnlen(self._collection)>=self._cursor+1defremove(self):if0<=self._cursor<len(self._collection):self._collection.remove(self._collection[self._cursor])else:self._raise_key_exception()classDictIterator(Iterator):""" Итератор, проходящий по словарю - из-за того, что словари в Python'е реализованы, как хеш-таблицы, порядок обхода может меняться во время разных запусков """_error=KeyErrordef__init__(self,collection:dict):super().__init__(collection,next(iter(collection)))self._keys=list(self._collection.keys())self._keys.pop(0)defcurrent(self):ifself._cursorinself._collection:returnself._collection[self._cursor]self._raise_key_exception()defnext(self):iflen(self._keys):self._cursor=self._keys.pop(0)returnself._collection[self._cursor]else:self._raise_key_exception()defhas_next(self):returnlen(self._keys)>0defremove(self):ifself._cursorinself._collection:delself._collection[self._cursor]try:self.next()exceptself._error:raiseKeyError('Collection of type {} is empty'.format(self.__class__.__name__))else:self._raise_key_exception()classCollection(metaclass=ABCMeta):""" Абстрактная коллекция """@abstractmethoddefiterator(self):passclassListCollection(Collection):""" Коллекция-обертка для обычного списка """def__init__(self,collection:list):self._collection=collectiondefiterator(self):returnListIterator(self._collection)classDictCollection(Collection):""" Коллекция-обертка для словаря """def__init__(self,collection:dict):self._collection=collectiondefiterator(self):returnDictIterator(self._collection)deftest(title=str,collection=Collection):print("\n{}\n".format(title))iterator=collection.iterator()print(iterator.current())iterator.next()print(iterator.next())iterator.remove()print(iterator.current())print(iterator.has_next())print()if__name__=='__main__':print('OUTPUT:')test('List testing',ListCollection([1,2,3,4,5]))test('Dictionary testing',DictCollection({'a':1,'b':2,'c':3,'f':8}))'''OUTPUT:List testing134TrueDictionary testing132False'''