Я пытаюсь разобраться в итераторах SPL, и я придумал два способа справиться с этим. Я считаю, что первая версия менее сложна, но вторая версия имеет композиционный характер (я думаю).
Чего я не вижу, какой из них предпочтительнее другого? Или я просто слишком усложняю это?
Вот мои мысли:
Объект реализует итератор:
class BoxOfChocolates implements Iterator {
private $id
private $name; // e.g. Valentine's Heart Box
private $maker; // e.g. Hersheys
private $items; // array
public function getChocolates() {
$query = ...
foreach($rows as $row) {
$this->_items[] = new Chocolate() ...
}
}
// ... necessary iterator implementation stuff
}
Объект содержит объект с возможностью итерации:
class BoxOfChocolates {
private $id;
private $name;
private $maker;
private $items; // chocolates object
public function getChocolates() {
$this->items = new Chocolates();
$this->items->getChocolates();
}
}
class Chocolates implements Iterator {
private $items;
public function getChocolates() {
$query = ...
foreach($rows as $row) {
$this->_items[] = new Chocolate() ...
}
}
// ... necessary iterator implementation stuff
}
Это зависит от ситуации. Коробка конфет будет содержать несколько коллекций? В таком случае вам нужно, чтобы коллекции были членами.
Подумайте об этом так. Коробка конфет - это коллекция (например, PersonList) или что-то, что владеет коллекцией (например, автомобиль может владеть коллекцией своих последних владельцев).
Я думаю, это относится к первой группе
:)
Я бы посоветовал и первую группу, если предположить, что ваша метафора о шоколаде довольно точна для реального класса.
коробка конфет - это действительно ваша коллекция конфет, и поэтому имеет смысл перебирать конфеты внутри этой коробки. добавление отдельного списка шоколада на самом деле не добавляет никакой ценности, а просто добавляет ненужный слой.
Ну, я не знаю PHP, поэтому я не могу правильно ответить на вопрос, но я попробую связать его с областью, которую я знаю.
В C# есть два интерфейса: IEnumerable и IEnumerator. В вашем примере BoxOfChocolates будет Enumerable, Chocolates будет Enumerator.
Enumerables просто имеют метод, который возвращает Enumerator. У счетчиков есть понятие текущего элемента и средства перехода к следующему элементу. В большинстве случаев класс Enumerator не «виден». Например, в строке «foreach (шоколадный элемент в boxOfChoclates)» boxOfChocolates является Enumerable; объект Enumerator полностью скрыт функцией foreach.
Проще говоря, контейнер - это место для реализации итератора.
Я думаю, вам следует хранить свои коллекции отдельно от итераторов. Я согласен с @James Curran, что в коллекциях часто есть итераторы - на самом деле, их может быть несколько. Например, вам может понадобиться итератор, который пропускает конфеты с орехами (хотя в типичном случае нужен итератор, который меняет порядок). В этом случае смысл метода next () меняется. Чтобы справиться с этим, реализуйте итераторы в отдельных классах, которые содержат свою собственную семантику итераций. Предоставьте в коллекции методы для получения итераторов правильного вида. Это действительно проблема разделения интересов. Коллекцию не волнует, как пользователь перебирает ее, это забота итератора.
bbxbby, лучшее решение - обычно самое простое. Вы определенно не слишком усложняете создание отдельного объекта-итератора. Фактически, PHP поддерживает и поощряет такое агрегирование, предоставляя интерфейс IteratorAggregate
. Объекты, реализующие IteratorAggregate
, должны содержать метод getIterator()
, возвращающий Iterator
. Это действительно то, что делает ваш метод getChocolated()
. Хорошая особенность IteratorAggregate
заключается в том, что вы можете передать его объект непосредственно в цикл foreach
. При использовании ваш код может выглядеть так:
class BoxOfChocolates implements IteratorAggregate
private $chocolates = array();
public function getIterator() {
return new ArrayIterator(new ArrayObject($this->chocolates)));
}
}
А затем где-то в коде:
$box = new BoxOfChocolates();
foreach ($box as $chocolate) { ... }