Iterator - 迭代器模式

目录

  • 前言
  • 回顾
  • UML 类图
  • 代码分析
  • 抽象的 UML 类图
  • 思考

前言

  这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢慢排好,从上往下一块块的拿起来(迭代),再一口气吃掉,这就是今天的早餐,也就是要说的 Iterator - 迭代器模式。

回顾

  我们常用的 for 和 foreach,其实就是 MS 给我们封装后的迭代器模式。为什么数组和集合能够使用这两个关键字呢?因为他们都实现了一个接口 IEnumerable,实现了内部方法 GetEnumerator。我们对一个集合,或者是数组进行遍历的同时,也就是数组或集合元素的下标不断递增的一个过程。

  左边的下标 0 表示数组的第一个元素;

  左边的下标 1 表示数组的第二个元素;

  ... ...

  左边的下标 i 表示数组的第i+1个元素;

  最后一个元素就是数组的长度 - 1;

  

UML 类图

代码分析

  IEnumerable 接口

    interface IEnumerable
{
IEnumerator GetEnumerator();
}

  这里只有一个方法 GetEnumerator(),该方法可以生成一个遍历集合的元素的迭代器。通过该迭代器,就可以进行集合元素的遍历了。

  IEnumerator 接口

    interface IEnumerator
{
bool MoveNext(); object GetCurrent();
}

  实现该接口的实例可以成为迭代器。这里有两个方法: MoveNext(),GetCurrent()。

  MoveNext():移动到下一个元素的下标,如果存在该下标(没有超出索引位置),则返回 true。主要用于终止循环条件。

  GetCurrent():获取当前集合元素的值,不过这里因为返回的类型为 object,可能需要进行强转,当然,你也可以选择使用泛型。

  Dish.cs 类(碟子)

    class Dish : IEnumerable
{
private readonly List<Aoliao> _aoliaos; public Dish()
{
_aoliaos = new List<Aoliao>();
} public IEnumerator GetEnumerator()
{
return new DishIterator(this);
} public void AppendAoliao(Aoliao aoliao)
{
_aoliaos.Add(aoliao);
} public int GetCount()
{
return _aoliaos.Count;
} public Aoliao GetAoliao(int index)
{
if (index >= GetCount())
{
throw new IndexOutOfRangeException();
} return _aoliaos[index];
}
}

  这是一个碟子类,因为它实现了 IEnumerable 接口,我把它当作集合,用于放置拆开包装后的奥利奥饼干。

  这里的构造函数,进行对 List<Aoliao> 进行集合的初始化。

  AppendAoliao(Aoliao aoliao):在原有的集合中追加新元素,在放置好的奥利奥饼干后再添加一块新的奥利奥饼干。

  GetCount():获取集合的个数,获取碟子上奥利奥饼干的总个数。

  GetAoliao(int index):根据下标获取集合中的元素。

  DishIterator.cs 类(碟子迭代器)

    class DishIterator : IEnumerator
{
private int _index;
private readonly Dish _dish; public DishIterator(Dish cookie)
{
_index = -;
_dish = cookie;
} public bool MoveNext()
{
_index++;
return _index < _dish.GetCount();
} public object GetCurrent()
{
try
{
return _dish.GetAoliao(_index);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}

  该类实现了 IEnumerator 接口,作为迭代器的一个实例对象,用于遍历 Dish 对象内集合的一个迭代器对象,这里有两个字段:

  _index:用于指定数组元素的下标,递增,注意,这里我选择让下标从 -1 开始。

  _dish:保存对 Dish 类的一个引用。

  MoveNext():移动到下一个元素的下标,递增下标 _index,假如索引超出界限则返回 false,从这里可以得知奥利奥饼干有没有吃完。

  GetCurrent():获取当前元素,根据下标 _index。

  Aoliao.cs 类(奥利奥饼干)

    class Aoliao
{
/// <summary>
/// 味道
/// </summary>
public bool Taste { get; set; }
}

  这里的 Taste 属性,我只用于标识它是否好吃。

  

  Main.cs 类

    class Program
{
static void Main(string[] args)
{
var dish = new Dish();
dish.AppendAoliao(new Aoliao() { Taste = true });
dish.AppendAoliao(new Aoliao() { Taste = true });
dish.AppendAoliao(new Aoliao() { Taste = true }); var iterator = dish.GetEnumerator();
while (iterator.MoveNext())
{
var aoliao = (Aoliao)iterator.GetCurrent();
Console.WriteLine("味道: " + aoliao.Taste);
} Console.Read();
}
}

  dish 作为一个数组,在一开始初始化的时候放置几块奥利奥饼干,通过 GetEnumerator() 可以得到迭代器,在 while 循环中,通过 MoveNext() 可以移动到集合的下一个元素下标,并取出奥利奥饼干,直到超出索引范围(即奥利奥饼干已经吃完)才会终止循环。这就是之前为什么我将 DishIterator 的下标(_index)初始化值为 -1,MoveNext() 方法会先移动光标的位置,再从迭代器的 GetCurrent() 方法取出当前元素的值(根据 MoveNext() 移动后的下标)。

抽象的 UML 类图

思考

  【1】为什么要使用 Iterator 迭代器模式呢?对于集合,或者数组,我们直接使用 for 和 foreach 不就可以了吗?

  观察上述代码,我们发现在 while 循环内只涉及方法 MoveNext() 和 GetCurrent(),不依赖集合本身的 Dish 类对象,在遍历时与集合没有强耦合的关系,遍历和实现进行了分离。

  也就是说,无论集合 Dish 本身如何变化,只要能够正常返回 iterator 迭代器,我们就可以正常遍历。

  设计模式的作用就是帮助我们编写可复用的类。所谓“可复用”,是指将类当成“组件”,当一个组件发生变化时,会尽可能的减少对其他组件的影响,其他组件只需更少的修改或者不需要修改就可以继续正常工作。

  

  【2】为什么我们有 ConcreteEnumerable 和 ConcreteIterator 两个具体类,还要额外创建一层接口呢?

  我们总是幻想着使用实体类来解决遇到的所有问题。如果只使用具体类来解决问题,很容易增加类之间的强耦合度,这部分类也难以当成组件多次利用。为了降低类之间的耦合度,为了增加类的利用度,从而引入了抽象类和接口。

  【总结】优先使用抽象类和接口来进行编程,而不要总想着采用具体类来实现编程。


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6550794.html

[C# 设计模式] Iterator - 迭代器模式:我与一份奥利奥早餐的故事的更多相关文章

  1. [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式

    Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...

  2. [C++设计模式] iterator 迭代器模式

    迭代器模式定义:提供一种方法顺序訪问一个聚合对象中各个元素,而又不须要暴露该对象. 迭代器分内部迭代器和外部迭代器.内部迭代器与对象耦合紧密,不推荐使用. 外部迭代器与聚合容器的内部对象松耦合,推荐使 ...

  3. C++设计模式-Iterator迭代器模式

    ref: http://www.cnblogs.com/onlycxue/archive/2013/12/25/3490738.html

  4. 设计模式之迭代器模式(Iterator)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  5. 乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern)

    原文:乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 迭代器模式(Iterator Pattern) 作者:weba ...

  6. 设计模式学习--迭代器模式(Iterator Pattern)和组合模式(Composite Pattern)

    设计模式学习--迭代器模式(Iterator Pattern) 概述 ——————————————————————————————————————————————————— 迭代器模式提供一种方法顺序 ...

  7. 设计模式17:Iterator 迭代器模式(行为型模式)

    Iterator 迭代器模式(行为型模式) 动机(Motivation) 在软件构建过程中,集合对象内部结构常常变化各异.但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码可以透 ...

  8. Python进阶:设计模式之迭代器模式

    在软件开发领域中,人们经常会用到这一个概念——“设计模式”(design pattern),它是一种针对软件设计的共性问题而提出的解决方案.在一本圣经级的书籍<设计模式:可复用面向对象软件的基础 ...

  9. 设计模式之——迭代器模式

    设计模式是开发者前辈们给我们后背的一个经验总结.有效的使用设计模式,能够帮助我们编写可复用的类.所谓"可复用",就是指将类实现为一个组件,当一个组件发生改变时,不需要对其他组件进行 ...

随机推荐

  1. linux mailbox模型

    一.device tree中的写法 二. mailbox框架 (driver/mailbox/mailbox.c) struct mbox_controller { struct device *de ...

  2. Linux系统下如何运行.sh文件

    在Linux系统下运行.sh文件有两种方法,比如我在root目录下有个datelog.sh文件 第一种(这种办法需要用chmod使得文件具备执行条件(x): chmod u+x datelog.sh) ...

  3. 一、开水白菜(steamed Chinese cabbage in supreme soup)

    菜品历史 相传,开水白菜是由颇受慈禧赏识的川菜名厨黄敬临在清宫御膳房创制的. 黄敬临当厨时,不少人贬损川菜"只会麻辣,粗俗土气",为了破谣立证,他冥思苦想多时并经由百番尝试,终于开 ...

  4. 梯度提升决策树(GBDT)

    1.提升树 以决策树为基函数的提升方法称为提升树.决策树可以分为分类树和回归树.提升树模型可以表示为决策树的加法模型. 针对不同的问题的提升术算法的主要区别就是损失函数的不同,对于回归问题我们选用平方 ...

  5. js中的cookie

    cookie就是一个存放数据的东西,存储量很小4kb,存放在客户端上和应用设备上. 应用场景 用户注册,用户登录,购物车. Chrome浏览器在计算机中存放cookie的位置 C:\Users\Adm ...

  6. Caused by: java.lang.ClassCastException: org.springframework.web.SpringServletContainerInitializer cannot be cast to javax.servlet.ServletContainerInitializer

    A child container failed during startjava.util.concurrent.ExecutionException: org.apache.catalina.Li ...

  7. Revisiting Network Support for RDMA

    重新审视RDMA的网络支持 本文为SIGCOMM 2018会议论文. 笔者翻译了该论文.由于时间仓促,且笔者英文能力有限,错误之处在所难免:欢迎读者批评指正. 本文及翻译版本仅用于学习使用.如果有任何 ...

  8. jenkins自动化工具使用教程

    自动化构建.测试.部署.代码检测越来越重要.主要有一下几点原因 1.  企业做大,项目变多,多端支持(web,h5,小程序等) 2.  微服务提倡高内聚低耦合,项目因拆分变多 3.  DevOps自动 ...

  9. kali linux学习笔记(四) : 网络端口大全介绍

    端口大全介绍 2端口:管理实用程序 3端口:压缩进程 5端口:远程作业登录 7端口:回显 9端口:丢弃 11端口:在线用户 13端口:时间 17端口:每日引用 18端口:消息发送协议 19端口:字符发 ...

  10. JSONP和CORS两种跨域方式的优缺点及使用方法原理介绍

    随着软件开发分工趋于精细,前后端开发分离成为趋势,前端同事负责前端页面的展示及页面逻辑处理,服务端同事负责业务逻辑处理同时通过API为前端提供数据也为前端提供数据的持久化能力,考虑到前后端同事开发工具 ...