• Java设计模式 (1) —— Iterator迭代器模式


            拿到这个话题,首先搞清楚:什么是迭代器?

            大家都学过 for-i 循环,for-i循环的本质就是 通过i++ 或者 i-- ,以实现 从数据的一端 一个一个地遍历数据元素,直到另一端的最后一个元素,将这里 i 的通用作用 抽象化后形成的设计,在设计中就可以叫做迭代器 Iterator;Iterate 在英语中有 "重复说,重复做" 的意思,如:

    1. we iterate through it with foreach.
    2. 我们重复做它,通过foreach。(倒过来读也行,我从不求完美。)

            Iterator模式的主要作用就是在数据集合中按照顺序一个一个地遍历集合,我们常说的迭代器,就是这个Iterator;假设我们现在需要做一个容器,底层可以使用链表,也可以使用数组,或者映射,或者其他类型,Anyhow 无论如何,肯定都需要为用户设计一些存取,访问遍历等基本操作。

           在程序设计中,由于底部容器集合的底层实现不同,各自不同的遍历方式,只有容器它自己知道它自己应该如何去遍历,我们无法将这些不同的容器,限制在某一种特定的遍历方式上,对于这种困境,我们就需要做一个统一存取的迭代器接口,让用户不用关系这些容器因为底层原因而造成有各种花里胡哨的的遍历实现,方便用户。

            Iterator 迭代器模式,就是一个专门提供给用户做容器元素访问迭代需求的设计模式。它的优点:

    • 避免了将不同底层的容器遍历访问限制死在某一种实现上的困境。
    • 不用暴露内部结构给用户,增加用户的易用性。

          至于,容器自己怎么遍历,只有容器它自己知道,所以,为了方便用户,我们就要求:让每个容器,自己提供它的迭代器接口,我们用户不用关心它的实现,用就好了。

            迭代器接口代码:

    1. /**
    2. * 遍历集合容器的接口。
    3. */
    4. public interface Iterator {
    5. boolean hasNext();//是否有下一个元素。
    6. Object next(); //获取元素,并指向下一个元素。
    7. }

            因此我们要求,凡是你觉得你自己是一个集合的类,麻烦您给我们用户提供一个获取迭代器的接口。

    1. /**
    2. * 表示集合的接口。
    3. */
    4. public interface AggregateCollection {
    5. /**
    6. * 为用户设计的,提供迭代器的方法。
    7. * @return 返回统一的迭代器。
    8. */
    9. Iterator iterator();
    10. }

    好了,集合容器 主角上场了。

    1. /**
    2. * 表示容器集合类
    3. * 根据约定,但凡 你觉得你自己要实现一个集合容器,你就需要实现 AggregateCollection 接口。
    4. * 以提供给用户获取迭代器的方法 iterator()
    5. */
    6. public class Container implements AggregateCollection {
    7. private Element[] elements;
    8. private int size = 0;
    9. //根据 默认的最大容量capacity 初始化 容器
    10. public Container(int maxCapacity) {
    11. this.elements = new Element[maxCapacity];
    12. }
    13. //访问容器中每个索引位置的元素。
    14. public Element getElementAt(int index) {
    15. return elements[index];
    16. }
    17. //Container有效元素,size 的维护机制:每次添加一个元素,元素总数cursor 要 + 1
    18. public void appendElement(Element element) {
    19. this.elements[size] = element;
    20. size++;
    21. }
    22. //获取有效元素的个数。
    23. public int getSize() {
    24. return size;
    25. }
    26. @Override
    27. public Iterator iterator() {
    28. return new ContainerIterator(this);
    29. }
    30. }

            这里提供属于容器的基础方法。为了弱化类之间的耦合,关于如何遍历的内容,专门提取到下面设计的ContainerIterator类中。

    1. /**
    2. * 容器实现遍历的类。
    3. 这里要弄明白2个为什么?
    4. 1.为什么要抽象出这个类,为了解耦 出遍历的操作。
    5. 2.为什么非要实现Iterator接口,为的就是将来返回Iterater接口给用户,
    6. 这样用户while的时候,拿到手的是Iterator接口对象,以实现和这类解耦。
    7. */
    8. public class ContainerIterator implements Iterator {
    9. // 引入容器对象,因为next和hashNext方法,该如何实现,只有container自己知道,因此迭代的操作需要容器它自己实现,所以我们请它进这个类。
    10. private Container container;
    11. private int index;
    12. //初始化容器迭代器。
    13. public ContainerIterator(Container container){
    14. this.container = container;
    15. this.index = 0;
    16. }
    17. @Override
    18. public boolean hasNext() {
    19. return index < container.getSize();
    20. }
    21. @Override
    22. public Object next() {
    23. return container.getElementAt(index++);
    24. }
    25. }

            容器中的基本元素对象类

    1. /**
    2. * 容器中的元素对象,加个名字,好遍历显示。
    3. */
    4. public class Element {
    5. private String name;
    6. public Element(String name) {
    7. this.name = name;
    8. }
    9. public String getName() {
    10. return name;
    11. }
    12. }

    好了,利用我们的迭代器的设计思想,程序基本已经设计完毕,通过程序测试一下,看看能否达到用户的预期:

    1. 站在用户的角度,至于你的集合容器底层如何具体实现,我不管。
    2. 凡是你设计的是一个Collection集合容器,你就要给我提供iterate方法,
    3. 用户只要拿到你的iterator,
    4. 使用的hashNext方法和next方法遍历到你容器中的所有元素。
    1. public class Main {
    2. public static void main(String[] args) {
    3. //集合容器Container 中 包含了 iterator 的功能。
    4. Container container = new Container(6);
    5. container.appendElement(new Element("Java"));
    6. container.appendElement(new Element("PHP"));
    7. container.appendElement(new Element("Javascript"));
    8. container.appendElement(new Element("Golang"));
    9. container.appendElement(new Element("CPP"));
    10. container.appendElement(new Element("Ruby"));
    11. Iterator it = container.iterator();
    12. while (it.hasNext()){
    13. Element element = (Element) it.next();
    14. System.out.println(element.getName());
    15. }
    16. }
    17. }

    console 控制台输出:

    1. Java
    2. PHP
    3. Javascript
    4. Golang
    5. CPP
    6. Ruby

            当用户在使用容器的时候,用户就会跟容器提一个遍历器要求,然后容器就给了用户一个遍历器接口的抽象化的对象,在while的时候使用,这种设计的优点是:使用Iterator接口对象,可让我们循环的时候,可以将遍历操作和具体的实现解耦而分离开来。如果编写容器的开发人员,想换一个数据结构来管理元素,至少使用迭代器的用户,他写的代码不用改。 

           编一个故事玩玩:

            话说,开天劈地之时,江湖大佬太上老君炼化了一个可以装各种宇宙元素的法宝:容器机器人,提供给各种技术修炼门派使用。

            有一天一个名叫用户的侠客,得到了这个法宝,小手一伸,对容器说:“江湖规矩 ! 遍历器!”。

            容器从腰间掏出了一块类似令牌的东西,递到用户手上,诺诺的说道:“有,有,有,必须有的!"。

            只见金灿灿的遍历器上写了 nexthasNext 两个大字!(英语不该用“两个大字”来形容的,我也知道,请别那么认真和刻薄。)

            用户满足的迷了一眼,笑着道:”嗯~~~ 不错 !  哈哈!有了这个对象,我就可以看到你过去和未来了! "。

            只见用户从腰间抽出 while 盒子,把这个法宝——遍历器嵌入盒子。纵身一跃,跳到一块大石之上,便开始盘腿修炼 。不知 口中念念有词地 念着什么.....

            “ hasNext ?next !    轰玛尼拜拜轰....   hasNext ?next ! .... 轰玛尼拜拜轰.... hasNext?next ! ...   ”

           不一会儿,只见用户双目一睁,纵身一跃,从大石上跳了下来,指着容器,神秘地对众人说:“我已经知道它的所有元素了。”

            请君勿笑段子差,文采有限,the key point is to understand this design pattern 。

            最后,点个题,Itrerator 是啥?就是各个技术流派 针对这个装元素的容器,为了满足这个不成文的江湖规矩  总结出来的招式,上面这个大概就是Java这个技术门派的招式了,毕竟也没上过大学,只能说是个大概 ,但是也要去尝试去推测一点:设计模式作为江湖规矩,本应该适用于任何技术门派。只敢说是本应该,因为我的生命有限的,而世界和宇宙的变化是无限的。

            尽管大家工作中好像都感觉没人在乎江湖规矩了,但是我建议你,最好还是学一下,免得嘲笑不懂江湖规矩。建议大家重视这种这个规矩,这个规矩不是来坑害你的,而是来成就你的。写一点点代码的小工程或许你看不出来,但是工程搞大了,就像spring一样,别人写了几十个模块,逻辑条理清晰,可能给你写,你写了一个模块,就乱的自己都搞不清了。

            这些就是会就是一个 从 难者不会 到 会者不难 的过程,毕竟谁没傻逼过?我有时候感觉,我觉得我自己超级傻逼,但是马上又会积极起来:只要你有一个颗想不断挑战心,没有谁能拦住你突破,最能拦住你的是你自己的内心给自己划分的那块疆域,不敢突破的心。

                   OK, That's all !大家加油,GoGo !  

  • 相关阅读:
    MySQL 索引优化及失效场景
    智慧工地源码 手册文档 app 数据大屏、硬件对接、萤石云
    大文件读取通常使用FileChannel,本文介绍FileChannel按行读取文本实例 java
    python爬取百度图片
    汽车红外夜视系统行业发展总体概况
    【算法3.12】二分图 - 匈牙利算法 NTR算法(完结)
    国内外17个学术论文网站推荐,记得收藏哦!
    mysql进阶:企业数据库安全防护方案
    变电站自动化系统中的安全措施分析及应用-安科瑞
    HtmlCss光标(插入符caret)透明隐藏光标 221106笔记
  • 原文地址:https://blog.csdn.net/wdw18668109050/article/details/127944634