前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >迭代器模式,更高大上的遍历体验!

迭代器模式,更高大上的遍历体验!

作者头像
用户6557940
发布2022-07-24 15:52:24
4200
发布2022-07-24 15:52:24
举报
文章被收录于专栏:Jungle笔记Jungle笔记

引言

写代码不少使用数组或者类似的集合对象吧?每次要遍历一遍数组怎么办?For 循环!或者while循环,一个一个访问每个位置的元素,直到数组末尾。STL里面甚至有专门的迭代器,针对具体的集合类对象,有对应使用的迭代器。STL的迭代器提供了丰富的遍历方法,如访问集合对象的首位元素、末位元素、指定位置的元素、下一个元素……怎么样,是不是感觉有了迭代器,遍历方法不再是难事了?

01

迭代器模式概述

遍历在日常编码过程中经常使用,通常是需要对一个具有很多对象实例的集合(称为聚合对象)进行访问或获取。比如要取聚合对象的首位元素、判断是否在聚合对象的末尾等。针对聚合对象的遍历,迭代器模式是一种很有效的解决方案,也是一种使用频率很高的设计模式。

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

通过引入迭代器,可以将数据的遍历功能从聚合对象中分离出来,这样一来,聚合对象只需负责存储数据,而迭代器对象负责遍历数据,使得聚合对象的职责更加单一,符合单一职责原则。

02

迭代器模式结构

迭代器模式结构中包含聚合和迭代器两个层次的结构。为方便扩展,迭代器模式常常和工厂方法模式结合。迭代器模式的UML图如下。有图可知,迭代器模式有以下几个角色:

  • Iterator(抽象迭代器):声明了访问和遍历聚合对象元素的接口,如first()方法用于访问聚合对象中第一个元素,next()方法用于访问下一个元素,hasNext()判断是否还有下一个元素,currentItem()方法用于获取当前元素。
  • ConcreteIterator(具体迭代器):实现抽象迭代器声明的方法,通常具体迭代器中会专门用一个变量(称为游标)来记录迭代器在聚合对象中所处的位置。
  • Aggregate(抽象聚合类):用于存储和管理元素对象,声明一个创建迭代器的接口,其实是一个抽象迭代器工厂的角色。
  • ConcreteAggregate(具体聚合类):实现了方法createIterator(),该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator的实例。

03

迭代器模式代码实例

电视机遥控器是迭代器的一个现实应用,通过它可以实现对电视频道集合的遍历操作,电视机可以看成一个存储频道的聚合对象。本例Jungle将采用迭代器模式来模拟遥控器操作电视频道的过程。

很明显,遥控器是一个具体的迭代器,具有上一个频道previous() 、下一个频道next()、当前频道currentChannel()等功能;需要遍历的聚合对象是电视频道的集合,即电视机。本例的UML图如下:

3.1.抽象聚合类和具体聚合类

代码语言:javascript
复制
#ifndef __AGGREGATE_H__
#define __AGGREGATE_H__

#include <vector>
using namespace std;

// 前向声明,因为两个类互相引用
class Iterator;
class RemoteControl;

// 抽象聚合类 Aggregate
class Aggregate
{
public:
  Aggregate(){}
  virtual Iterator* createIterator() = 0;
};

// 具体聚合类 Television
class Television :public Aggregate
{
public:
  Television();
  Television(vector<string> iChannelList);
  // 实现创建迭代器
  Iterator* createIterator();
  // 获取总的频道数目
  int getTotalChannelNum();
  void play(int i);
private:
  vector<string> channelList;
};

#endif //__AGGREGATE_H__

实现:

代码语言:javascript
复制
#include "Iterator.h"

Television::Television(){}

Television::Television(vector<string> iChannelList){
  this->channelList = iChannelList;
}

Iterator* Television::createIterator(){
  RemoteControl *it = new RemoteControl();
  it->setTV(this);
  return (Iterator*)it;
}

int Television::getTotalChannelNum(){
  return channelList.size();
}

void Television::play(int i){
  printf("现在播放:%s……\n", channelList[i].c_str());
}

3.2.抽象迭代器

代码语言:javascript
复制
// 抽象迭代器
class Iterator
{
public:
  Iterator(){}
  // 声明抽象遍历方法
  virtual void first() = 0;
  virtual void last() = 0;
  virtual void next() = 0;
  virtual void previous() = 0;
  virtual bool hasNext() = 0;
  virtual bool hasPrevious() = 0;
  virtual void currentChannel() = 0;
};

3.3.具体迭代器:RemoteControl

代码语言:javascript
复制
// 遥控器:具体迭代器
class RemoteControl :public Iterator
{
public:
  RemoteControl(){}
  void setTV(Television *iTv){
    this->tv = iTv;
    cursor = -1;
    totalNum = tv->getTotalChannelNum();
  }
  // 实现各个遍历方法
  void first(){
    cursor = 0;
  }
  void last(){
    cursor = totalNum - 1;
  }
  void next(){
    cursor++;
  }
  void previous(){
    cursor--;
  }
  bool hasNext(){
    return !(cursor == totalNum);
  }
  bool hasPrevious(){
    return !(cursor == -1);
  }
  void currentChannel(){
    tv->play(cursor);
  }
private:
  // 游标
  int cursor;
  // 总的频道数目
  int totalNum;
  // 电视
  Television* tv;
};

3.4.客户端代码示例及结果

代码语言:javascript
复制
#include <iostream>
#include "Iterator.h"

int main()
{
  vector<string> channelList = { "新闻频道", "财经频道", "体育频道", "电影频道", "音乐频道", "农业频道", "四川卫视", "成都卫视" };
  // 创建电视
  Television *tv = new Television(channelList);
  // 创建遥控器
  Iterator *remoteControl = tv->createIterator();

  // 顺序遍历
  printf("顺序遍历:\n");
  remoteControl->first();
  // 遍历电视所有频道
  while (remoteControl->hasNext()){
    remoteControl->currentChannel();
    remoteControl->next();
  }

  printf("\n\n");

  // 逆序遍历
  printf("逆序遍历:\n");
  remoteControl->last();
  // 遍历电视所有频道
  while (remoteControl->hasPrevious()){
    remoteControl->currentChannel();
    remoteControl->previous();
  }

  printf("\n\n");
  system("pause");
  return 0;
}

结果如下图:

04

总结

观察上述代码可发现,迭代器类和聚合类存在相互包含相互引用的关系,因此代码里需要前向声明某个类(具体操作见上,代码资源见https://github.com/FengJungle/DesignPattern)。

优点:

  • 支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多个遍历方式。
  • 简化了聚合类,使得聚合类的职责更加单一;
  • 迭代器模式中引入抽象层,易于增加新的迭代器类,便于扩展,符合开闭原则。

缺点:

  • 将聚合类中存储对象和管理对象的职责分离,增加新的聚合类时同样需要考虑增加对应的新的迭代器类,类的个数成对增加,不利于系统管理和维护;
  • 设计难度较大,需要充分考虑将来系统的扩展。

适用环境:

以下场景可以考虑使用迭代器模式:

  • 访问一个聚合对象而无需暴露它的内部结构;
  • 需要为一个聚合对象提供多种遍历方法。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Jungle笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3.1.抽象聚合类和具体聚合类
  • 3.2.抽象迭代器
  • 3.3.具体迭代器:RemoteControl
  • 3.4.客户端代码示例及结果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档