我们都用过电视机遥控器,通过它我们可以进行开机、关机、换台、改变音量等操作。我们可以将电视机看做一个存储电视频道的集合对象,通过遥控器可以对电视机中的频道集合进行操作,例如返回上一个频道、跳转到下一个频道或者跳转到指定的频道等。遥控器的出现,使得用户不需要知道这些频道到底如何存储在电视机中。在软件开发中也存在类似于电视机一样的类,他们可以存储了多个成员对象(元素),这些类通常称为聚合类(Aggregate Class),对应的对象称为聚合对象。为了更加方便地操作这些聚合对象,同时可以很灵活地为聚合对象增加不同的遍历方法,也需要类似于电视机遥控器一样的角色,可以访问一个聚合对象中的元素担忧部需要暴露它的内部结构,这就是我们需要学习的迭代器模式。

迭代器模式(Iterator) 学习难度:★★★☆☆ 使用频率:★★★★★

一、销售管理系统中数据的遍历

Background : M公司为某商场开发了一套销售管理系统,在对该系统进行分析和设计时,M公司开发人员发现经常需要对系统中的商品数据、客户数据等进行遍历,为了复用这些遍历代码,M公司开发人员设计了一个抽象的数据聚合类AbstractObjectList,而将存储商品和客户登记的类作为其子类。AbstractObjectList类结构如下图所示。

在上图中,IList类型的对象objects用于存储数据,AbstractObjectList类的方法说明如下表所示:

AbstractObjectList类的子类ProductList和CustomerList分别用于存储商品数据和客户数据。

  M公司开发人员通过对AbstractObjectList类结构进行分析,发现该设计方案存在以下问题:

  (1)在该类中,AddObject()与RemoveObject()等方法用于管理数据,而GetNextItem()、GetPreviousItem()、IsFirst()等方法又用于遍历数据,导致了聚合类的职责过重,违反了单一职责原则。

  (2)如果将抽象聚合类声明为一个接口,则在这个接口中充斥着大量方法,不利于子类实现,违反了接口隔离原则。

  (3)如果将所有的遍历操作都交给子类来实现,将导致子类代码过于庞大,而且必须暴露AbstractObjectList类的内部存储细节,向子类公开自己的私有属性,否则子类无法实施对数据的遍历,将破坏AbstractObjectList类的封装性。

  如何解决该问题?解决方案之一就是将聚合类中负责遍历数据的方法提取出来,封装到专门的类中,实现数据存储和数据遍历的分离,无须暴露聚合类的内部属性即可对其进行操作,这正是迭代器模式的意图所在。

二、迭代器模式概述

2.1 迭代器模式简介

  在软件开发中,经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据,二是遍历数据。从依赖性来看,前者是聚合对象的基本职责,而后者既是可变化的又是可分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称为“迭代器”的对象中,由迭代器来提供遍历聚合对象内部数据的行为,这将简化聚合对象的设计,更加符合单一职责原则。

迭代器(Iterator)模式:提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。  

2.2 迭代器模式结构

  (1)Iterator(抽象迭代器):定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法。

  (2)ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历。

  (3)Aggregate(抽象聚合类):用于存储和管理元素对象,声明一个CreateIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。

  (4)ConcreteAggregate(具体聚合类):实现了在抽象聚合类中声明的CreateIterator()方法,返回一个对应的具体迭代器ConcreteIterator实例。

三、销售管理系统中数据的遍历实现

3.1 重构后的设计结构

  其中,AbstractObjectList充当抽象聚合类,ProductList充当具体聚合类,AbstractIterator充当抽象迭代器,ProductIterator充当具体迭代器。

3.2 重构后的代码实现

  (1)抽象聚合类:AbstractObjectList

    /// <summary>
/// 抽象聚合类:AbstractObjectList
/// </summary>
public abstract class AbstractObjectList
{
protected IList<object> objectList = new List<object>(); public AbstractObjectList (IList<object> objectList)
{
this.objectList = objectList;
} public void AddObject(object obj)
{
this.objectList.Add(obj);
} public void RemoveObject(object obj)
{
this.objectList.Remove(obj);
} public IList<Object> GetObjectList()
{
return this.objectList;
} // 声明创建迭代器对象的抽象工厂方法
public abstract AbstractIterator CreateIterator();
}

  (2)具体聚合类 - ProductList 与 具体迭代器 - ProductIterator => 这里采用了内部类的方式

    /// <summary>
/// 具体聚合类:ProductList
/// </summary>
public class ProductList : AbstractObjectList
{
public ProductList(IList<object> objectList) : base(objectList)
{
} public override AbstractIterator CreateIterator()
{
return new ProductIterator(this);
} /// <summary>
/// 内部类=>具体迭代器:ProductIterator
/// </summary>
private class ProductIterator : AbstractIterator
{
private ProductList productList;
private IList<object> products;
private int cursor1; // 定义一个游标,用于记录正向遍历的位置
private int cursor2; // 定义一个游标,用于记录逆向遍历的位置 public ProductIterator(ProductList productList)
{
this.productList = productList;
this.products = productList.GetObjectList(); // 获取集合对象
this.cursor1 = ; // 设置正向遍历游标的初始值
this.cursor2 = this.products.Count - ; // 设置逆向遍历游标的初始值
} public object GetNextItem()
{
return products[cursor1];
} public object GetPreviousItem()
{
return products[cursor2];
} public bool IsFirst()
{
return cursor2 == -;
} public bool IsLast()
{
return cursor1 == products.Count;
} public void Next()
{
if (cursor1 < products.Count)
{
cursor1++;
}
} public void Previous()
{
if (cursor2 > -)
{
cursor2--;
}
}
}
}

  (3)抽象迭代器:AbstractIterator

    /// <summary>
/// 抽象迭代器:AbstractIterator
/// </summary>
public interface AbstractIterator
{
void Next(); // 移动至下一个元素
bool IsLast(); // 判断是否为最后一个元素
void Previous(); // 移动至上一个元素
bool IsFirst(); // 判断是否为第一个元素
object GetNextItem(); // 获取下一个元素
object GetPreviousItem(); // 获取上一个元素
}

  (4)客户端测试

    public class Program
{
public static void Main(string[] args)
{
IList<object> products = new List<object>();
products.Add("倚天剑");
products.Add("屠龙刀");
products.Add("断肠草");
products.Add("葵花宝典");
products.Add("四十二章经"); AbstractObjectList objectList = new ProductList(products); // 创建聚合对象
AbstractIterator iterator = objectList.CreateIterator(); // 创建迭代器对象 Console.WriteLine("正向遍历");
while (!iterator.IsLast())
{
Console.Write(iterator.GetNextItem() + ",");
iterator.Next();
} Console.WriteLine();
Console.WriteLine("-------------------------------------------------------");
Console.WriteLine("逆向遍历");
while (!iterator.IsFirst())
{
Console.Write(iterator.GetPreviousItem() + ",");
iterator.Previous();
} Console.ReadKey();
}
}

  F5编译运行后的结果如下图所示:

  

四、迭代器模式小结

4.1 主要优点

  (1)支持以不同方式遍历一个聚合对象,在同一个聚合对象上可以定义多种便利方式。

  (2)增加新的聚合类和迭代器类都很方便 => 无须修改原有代码,符合开闭原则。

4.2 主要缺点

  增加新的聚合类需要对应增加新的迭代器类 => 类的个数会成对增加!

4.3 应用场景

  (1)访问一个聚合对象的内容而无须暴露它的内部表示。

  (2)需要为一个聚合对象提供多种遍历方式。

  (3)重点 => 该模式在.Net中,可以通过实现IEnumberable接口即可,不再需要单独实现! (在.NET下,迭代器模式中的聚集接口和迭代器接口都已经存在了,其中IEnumerator接口扮演的就是迭代器角色,IEnumberable接口则扮演的就是抽象聚集的角色,其中定义了GetEnumerator()方法。)

参考资料

  

  (1)刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

  (2)圣杰,《C#设计模式之迭代器模式》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

设计模式的征途—21.迭代器(Iterator)模式的更多相关文章

  1. 设计模式C++描述----20.迭代器(Iterator)模式

    一. 举例说明 我们知道,在 STL 里提供 Iterator 来遍历 Vector 或者 List 数据结构. Iterator 模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个 ...

  2. Java 实现迭代器(Iterator)模式

    类图 /** * 自己定义集合接口, 相似java.util.Collection * 用于数据存储 * @author stone * */ public interface ICollection ...

  3. 设计模式之第21章-状态模式(Java实现)

    设计模式之第21章-状态模式(Java实现) “what are you 干啥了?怎么这么萎靡不振?”“昨晚又是补新番,又是补小笼包,睡得有点晚啊.话说杨过的那个雕兄真是太好了,每天给找蛇胆,又陪练武 ...

  4. 1、迭代器 Iterator模式 一个一个遍历 行为型设计模式

    1.Iterator模式 迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或者阵列)上遍访的接口,设计人员无需关心容器的内容. I ...

  5. 设计模式—迭代器Iterator模式

    什么是迭代器模式? 让用户通过特定的接口访问容器的数据,不需要了解容器内部的数据结构. 首先我们先模仿集合中ArrayList和LinkedList的实现.一个是基于数组的实现.一个是基于链表的实现, ...

  6. 设计模式——迭代器(Iterator)模式

    概述 迭代器模式简单的说(按我目前的理解)就是一个类提供一个对外迭代的接口,方面调用者迭代.这个迭代接口至少包括两个方法:hasNext()--用于判断是否还有下一个,next()--用于取出下一个对 ...

  7. Head First 设计模式 —— 10. 迭代器 (Iterator) 模式

    思考题 public void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList ...

  8. 《图解设计模式》读书笔记1-1 Iterator模式

    目录 迭代器模式的类图 类图的解释 迭代器模式的代码 解释 原因 思想 迭代器模式的类图 类图的解释 名称 说明 Aggregate 集合接口,有提供迭代器的方法 Iterator 迭代器接口,提供迭 ...

  9. 设计模式C++描述----21.解释器(Iterpreter)模式

    一. 解释器模式 定义:给定一个语言,定义它的文法的一种表示,并定一个解释器,这个解释器使用该表示来解释语言中的句子. 结构如下: 代码如下: //包含解释器之外的一些全局信息 class Conte ...

随机推荐

  1. Oracle之 any 、some、all解析

    oracle之 any.some.all 解析 因为很少用到, 所以几乎忘记了这几个函数, 不过它们还是很有用的使用它们可以大大简化一些SQL文的语法, 至于效率问题, 如CCW所说它们和EXISTS ...

  2. 关于安全性问题:(XSS,csrf,cors,jsonp,同源策略)

    关于安全性问题:(XSS,csrf,cors,jsonp,同源策略) Ajax 是无需刷新页面就能从服务器获取数据的一种方法.它的核心对象是XHR,同源策略是ajax的一种约束,它为通信设置了相同的协 ...

  3. linux RPM软件包管理

    linux RPM软件包管理 目录 1.软件包的介绍 2.rpm包管理 3.rpm包管理前端工具 1.软件包的介绍 在我们在对软件包管理前,先对软件包有个很好的了解,这样更方便我们来对其进行管理. 软 ...

  4. Echarts数据可视化series-map地图,开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

  5. JVM性能调优,GC

    刚刚做完了一个项目的性能测试,“有幸”也遇到了内存泄露的案例,所以在此和大家分享一下. 主要从以下几部分来说明,关于内存和内存泄露.溢出的概念,区分内存泄露和内存溢出:内存的区域划分,了解GC回收机制 ...

  6. PhoneWindow,ViewRoot,Activity之间的大致关系

    http://www.nowamagic.net/academy/detail/50160216 在android里,我们都知道activity.但是一个activity跟一个Window是一个什么关 ...

  7. (转)log4j使用介绍

    原文出自: log4j使用介绍 日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日志记录.以下是个人经验,具体请参考Log4j文档指南. Log4j ...

  8. iOS下OpenCV开发用OC还是Swift

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 其实标题中这个问题并不准确,准确的说法应该是iOS下的OpenCV开发是使用OC还是Swift ...

  9. mysql 安装配置

    l  下载压缩包: 官网下载地址:https://dev.mysql.com/downloads/mysql/5.1.html#downloads 第一步: 将下载的压缩包解压到你要安装的目录下 第二 ...

  10. 本表触发更新modifytime,跨表更新modifytime 触发器

    一.每行有改动,则触发更新modifytime SQL> create table test(id int, name varchar(10), crdate date, udate date) ...