Должен ли объект реализовывать итератор или содержать другой объект, который реализует итератор - PullRequest
5 голосов
/ 19 октября 2008

Я пытаюсь разобраться с итераторами SPL, и у меня есть 2 способа справиться с этим. Я вижу, что первая версия менее сложна, но вторая версия имеет ощущение композиции (я думаю).

Что я не вижу, какой из них предпочтительнее другого? Или я просто слишком усложняю это?

Вот мои мысли:

Объект реализует итератор:

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

}

Ответы [ 6 ]

4 голосов
/ 20 октября 2008

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) { ... }
2 голосов
/ 19 октября 2008

Это зависит от ситуации. Коробка шоколадных конфет собирается содержать несколько коллекций? Если это так, вам нужно, чтобы коллекции были членами.

Думай об этом так. Является ли коробка конфет коллекцией (т. Е. PersonList) или чем-то, что владеет коллекцией (т. Е. Машине может принадлежать коллекция ее последних владельцев).

Я думаю, это относится к первой группе

:)

1 голос
/ 19 октября 2008

Я бы тоже предложил первую группу, если предположить, что ваша шоколадная метафора довольно точна для фактического класса.

коробка конфет действительно является вашей коллекцией конфет, и поэтому имеет смысл захотеть перебрать конфеты внутри этой коробки. добавление отдельного списка шоколада на самом деле не добавляет никакой ценности, а просто добавляет ненужный слой.

0 голосов
/ 19 октября 2008

Я думаю, вы должны хранить свои коллекции отдельно от своих итераторов. Я бы согласился с @James Curran, что коллекции часто имеют итераторы - на самом деле их может быть несколько. Например, вам может понадобиться итератор, который пропускает конфеты с орехами (хотя типичный случай - это тот, который меняет порядок) В этом случае значение метода next () изменяется. Чтобы справиться с этим, реализуйте итераторы в отдельных классах, которые содержат свою семантику итерации. Предоставьте методы в коллекции для получения итераторов правильного вида. Это действительно проблема разделения интересов. Коллекция не заботится о том, как пользователь выполняет итерацию, это проблема итератора.

0 голосов
/ 19 октября 2008

Просто контейнер - это место для реализации итератора.

0 голосов
/ 19 октября 2008

Ну, я не знаю PHP, поэтому я не могу правильно ответить на вопрос, но я попытаюсь связать его с областью, которую я знаю.

В C # есть два интерфейса: IEnumerable и IEnumerator. В вашем примере BoxOfChocolates будет Enumerable, а Chocolates будет Enumerator.

У перечислимых просто есть метод, который возвращает перечислитель. Перечислители имеют понятие текущего элемента и средства перехода к следующему элементу. В большинстве случаев класс Enumerator не «виден». Например, в строке «foreach (Шоколадный элемент в boxOfChoclates)» boxOfChocolates представляет собой Enumerable; объект Enumerator полностью скрыт foreach.

...