我们都用过电视机遥控器,通过它我们可以进行开机、关机、换台、改变音量等操作。我们可以将电视机看做一个存储电视频道的集合对象,通过遥控器可以对电视机中的频道集合进行操作,例如返回上一个频道、跳转到下一个频道或者跳转到指定的频道等。遥控器的出现,使得用户不需要知道这些频道到底如何存储在电视机中。在软件开发中也存在类似于电视机一样的类,他们可以存储了多个成员对象(元素),这些类通常称为聚合类(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. C-一行或多行文章垂直居中

    1 样式效果 2 table布局 li span 

  2. 使用keepalived使用主备热备份功能

    图: 配置文件: 主服务器的配置如下: global_defs { router_id NodeA}vrrp_instance VI_1 { state MASTER #设置为主服务器 interfa ...

  3. 使用Canvas制作时钟动画

    复习Javascript到Canvas的知识点,看到一个使用Canvas绘制的静态时钟例子,便想将其变成动态显示系统时间的时钟动画.另外再配上数字显示的时钟,一个小的时钟模块的诞生了!目前的界面还比较 ...

  4. JavaScript面向对象基础与this指向问题

      前  言           我们的程序语言经历了从"面向机器".到"面向过程".再到"面向对象"的一个过程.而JavaScript是一 ...

  5. vue学习前奏——webpack

    "工欲善其事必先利其器",要想学习vue,首先需要我们去了解webpack,便于后期快速构建运行项目.废话不多说,下面开始介绍在开始一个vue项目前我们需要对webpack有一定的 ...

  6. JS对象深度克隆

    首先看一个例子: var student = { name:"yxz", age:25 } var newStudent = student; newStudent.sex = & ...

  7. Kotlin——最详细的环境搭建

    众所周知,Kotlin出来已经有一段时间了.Kotlin有着众多优势,不管是用于Android开发中,还是Java开发,都能缩减很大的代码量,大大提高了工作效率.而小生本人也是才从忙碌的个工作中抽身出 ...

  8. 【ASP.NET MVC 学习笔记】- 08 URL Routing

    本文参考:http://www.cnblogs.com/willick/p/3343105.html 1.URL Routing告诉MVC如何正确的定位Controller和Action. 2.URL ...

  9. 实现一个单隐层神经网络python

    看过首席科学家NG的深度学习公开课很久了,一直没有时间做课后编程题,做完想把思路总结下来,仅仅记录编程主线. 一 引用工具包 import numpy as np import matplotlib. ...

  10. LeetCode 1. Two Sum (两数之和)

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...