20.2迭代器模式

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
迭代器模式的使用场景:

  • 当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。
  • 你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。

也就是说,为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。
现在高级编程语言如 C#、JAVA 等本身已经把这个模式做在语言中了。不管如何,学习一下GoF的迭代器模式的基本结构,还是很有学习价值的。研究历史是为了更好地迎接未来。

20.3迭代器实现

迭代器模式(Iterator)结构图 第二十章 想走?可以!先买票——迭代器模式 - 图1代码示例
Iterator迭代器抽象类

  1. abstract class Iterator
  2. {
  3. abstract function First();
  4. abstract function Next();
  5. abstract function IsDone();
  6. abstract function CurrentItem();
  7. }

Aggregate聚集抽象类

  1. abstract class Aggregate
  2. {
  3. abstract function CreateIterator();
  4. }

ConcreteIterator具体迭代器类,继承Iterator

  1. class ConcreteIterator extends Iterator
  2. {
  3. //定义一个具体聚集对象
  4. private $aggregate = null;
  5. private $current = 0;
  6. //初始化时将具体的聚集对象传入
  7. public function __construct($aggregate)
  8. {
  9. $this->aggregate = $aggregate;
  10. }
  11. //得到聚集的第一个对象
  12. public function First()
  13. {
  14. return $this->aggregate[0];
  15. }
  16. //得到聚集的下一个对象
  17. public function Next()
  18. {
  19. $ret = null;
  20. $this->current ++;
  21. if ($this->current < count($this->aggregate)) {
  22. $ret = $this->aggregate[$this->current];
  23. }
  24. return $ret;
  25. }
  26. //判断当前是否遍历到结尾,到结尾返回true
  27. public function IsDone()
  28. {
  29. return $this->current >= count($this->aggregate) ? true : false;
  30. }
  31. //返回当前的聚集对象
  32. public function CurrentItem()
  33. {
  34. return $this->aggregate[$this->current];
  35. }
  36. }

ConcreteAggregate具体聚集类 继承Aggregate

  1. class ConcreteAggregate extends Aggregate
  2. {
  3. private $items = array();
  4. public function CreateIterator()
  5. {
  6. return new ConcreteIterator($this);
  7. }
  8. //返回聚集总个数
  9. public function Count()
  10. {
  11. return count($this->items);
  12. }
  13. public function setItems($value)
  14. {
  15. $this->items[] = $value;
  16. }
  17. public function getItems()
  18. {
  19. return $this->items;
  20. }
  21. }

ConcreteIteratorDesc 反向遍历具体迭代器类,继承Iterator

  1. class ConcreteIteratorDesc extends Iterator
  2. {
  3. private $aggregate = null;
  4. private $current = 0;
  5. public function __construct($aggregate)
  6. {
  7. $this->aggregate = $aggregate;
  8. $this->current = count($this->aggregate) - 1;
  9. }
  10. public function First()
  11. {
  12. return $this->aggregate[$this->current];
  13. }
  14. public function Next()
  15. {
  16. $ret = null;
  17. $this->current --;
  18. if ($this->current >= 0) {
  19. $ret = $this->aggregate[$this->current];
  20. }
  21. return $ret;
  22. }
  23. public function CurrentItem()
  24. {
  25. return $this->aggregate[$this->current];
  26. }
  27. public function IsDone()
  28. {
  29. return $this->current < 0 ? true : false;
  30. }
  31. }

客户端代码

  1. public function iteratorImp()
  2. {
  3. $a = new ConcreteAggregate();
  4. $a->setItems('大鸟');
  5. $a->setItems('小菜');
  6. $a->setItems('行李');
  7. $a->setItems('老外');
  8. $a->setItems('公交内部员工');
  9. $a->setItems('小偷');
  10. //此处切换遍历方式
  11. $i = new ConcreteIterator($a->getItems());
  12. //$i = new ConcreteIteratorDesc($a->getItems());
  13. $i->First();
  14. while (!$i->IsDone()) {
  15. echo $i->CurrentItem() . '请买车票!' . PHP_EOL;
  16. $i->Next();
  17. }
  18. }

运行结果

  1. 大鸟请买车票!
  2. 小菜请买车票!
  3. 行李请买车票!
  4. 老外请买车票!
  5. 公交内部员工请买车票!
  6. 小偷请买车票!

20.4.NET的迭代器实现(拓展)

.NET迭代器代码实现
IEumerator 支持对非泛型集合的简单迭代接口。

  1. public function IEumerator
  2. {
  3. object Current
  4. {
  5. get;
  6. }
  7. boll MoveNext();
  8. //恢复初始化指向的位置,该位置位于集合中第一个元素之前
  9. void Reset();
  10. }

IEnumerable 公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。

  1. public interface IEnumerable
  2. {
  3. //返回一个循环访问集合的枚举数
  4. IEumerator GetEnumerator();
  5. }

foreach in应用代码。

  1. static void Main(string[] args)
  2. {
  3. IList<string> a=new List<string>();
  4. a.Add("大鸟");
  5. a.Add("小菜");
  6. a.Add("行李");
  7. a.Add("老外");
  8. a.Add("公交内部员工");
  9. a.Add("小偷");
  10. foreach (string item in a)
  11. {
  12. Console.WriteLine("{0} 请买车票!", item);
  13. }
  14. Console.Read();
  15. }

foreach in 在编译器里做的工作。

  1. IEnumerator<string> e = a.GetEnumerator();
  2. while (e.MoveNext())
  3. {
  4. Console.WriteLine("{0} 请买车票!", e.Current);
  5. }

foreach in 就是实现这两个接口来实际循环遍历。
总的来说,迭代器(Iterator)模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常普遍的应用,但由于它太普遍了,所以各种高级语言都对它进行了封装,所以反而给人感觉此模式本身不太常用了。