Chapter 9 the Iterator and Composite Patterns——Well-Managed Collections

总述

本章所讨论的是把对象装进聚集(collections)里面。
当你想把多个来源不同的代码整合到一起时,你可能会想对这些源码做一些修改,但是任何一个源码的提供者都不希望你这样做。
因为你会更改已经存在的代码,这不符合前面所讲的原则。

The Iterator Pattern

它是用一个抽象的接口去适配各种不同的容器,这样一来就屏蔽了各容器自身的遍历方法,转而使用通用的遍历手段来进行遍历。
具体的方法就是某具体的容器类只要继承这个iterator接口即可。
这些具体的容器也被称为聚合。

Adding an Iterator to DinerMenu

具体做法分为如下几步:
1、定义一个Iterator接口,里面含有hasNext()和next()方法,前者判断是否还有元素,后者则是取下一元素。
2、具体的容器迭代器类要去实现这个Iterator接口。
3、在具体的容器类中创建一个方法,一般叫createIterator,来返回该容器的迭代器。
经过这些步骤以后,我们再也不用再关注具体容器的具体实现细节了,因为它们各自的迭代器能够带替它们为我们服务,而且这些迭代器能让我们更加专注于自己的本分。
但是,问题来了,一个容器对应一个迭代器,那么n个容器岂不是要对应n个容器吗?我们还要管理这n个迭代器吗?这很烦呐。
我们只想管理唯一的一个迭代器,那么这一目的如何达到呢?

Making some improvements

所谓改进就是为各种不同的Iterator建立一个公共的接口。
Iterator接口中一般还含有一个remove方法,它用来移除最后一个元素。
在你遍历时如果集合发生变动得到的结果是不确定的,这是在多线程环境下而言的。
P373类图可以清晰地表达如何进一步解决公共接口的问题。
首先,具体的功能类都去实现一个抽象的功能接口,然后具体的功能类的Iterator类也都去实现公共的Iterator接口(当然,在本书中作者采用了Java提供的现成的Iterator接口),这样它们直接套用公共的Iterator就好了。
实际上这个公共的Iterator就是Java提供的接口。

Iterator Pattern defined

它的官方定义为:它提供了一种方式,这种方式可以在不暴露内部细节的前提下,顺序地访问聚类中的对象。它把遍历的任务加到了Iterator上,这简化了聚类的接口,并且把聚类应该负担的责任转交给了聚类。
Iterator Pattern有点像factory pattern。
你可以扩展共有的Iterator接口。
迭代器模式

Single Responsibility

这个原则内容:一个类只有一个可以改变的原因。
一个类应该有且只有一种功能,因为一个类的功能越多,那么它引入问题的几率越大。
类的每个功能都是不确定因素,一个类具有很多功能就意味着它有很多不确定因素,那么它被改写的可能性越高。
严格遵守这一原则可以让模块达到高内聚低耦合。
在不使用Iterator模式的情况下,用容器编写数据类会返回容器类型对象,这会破坏数据的封装性。而使用迭代器则避免了内部全部数据的暴露。
其实,这么一来客户依然没有摆脱来自一个不同的数据容器就增加一个容器对象的窘境,只不过通过Iterator模式把各种不同容器之间的类型统一为同一个类型而已,就是说一个容器类型仍然需要一个迭代器。
这样做的好处是让客户与容器对象的内部细节之间的耦合消除。

Iterators and Collections in Java5

在Java5中有一种for/in循环,可以实现容器的遍历,它的用法有点像foreach。
我又不是搞Java的,所以不用太深入了解。
正如我上边所说的,你每增加一个容器都需要重新修改客户的代码,这一点非常不好。
我们需要一种方法来统一管理这些不同的容器对象。

The Composite Pattern

Iterator Pattern的确在一定程度上解决了因遍历不同类型容器而带来的不兼容问题,但是它对各种容器自身的变化的适应性不强。
如果一个容器突然想包括另外一个不同类型的容器,这是没法做到的,因为它们之间的类型不兼容,而Iterator Pattern没法解决这个问题。
那么现在就需要复合模式了,所以由此可见复合可以解决类型不兼容的问题。
复合模式的官方定义:复合模式允许你把一个对象复合进一个树状结构来表示一种一对多的关系,这种模式可以让客户一致地对待对象和复合进来的对象。
这种模式可以让我们只需写简单的代码就可以操纵整棵树。
这种模式可以让我们忽略对象的复合和单体对象之间的区别。
P396的图展示了这种设计模式的组织结构。
客户通过操纵Component接口来操纵整棵树,除了叶子节点外,其他的都被称为复合。叶子节点只负责处理操作,它不负责处理上下层次的关系,而复合则需要处理这两种关系。

如何将The Composite Pattern和Iterator Pattern合二为一?

在Composite Pattern中component是操作对象的接口,它不仅声明了用于操作Composite Pattern本身的方法而且还添加了Iterator遍历的必要方法。
所有的component必须实现面向客户的接口,在这里具体为MenuComponent接口,由于节点和叶子经常会有自己的方法,它们可能重写继承而来的方法。
这里说最好的办法就是你在默认的方法中抛出一个运行时异常就可以了,如果孩子节点需要的话它们就直接用,否则自己实现自己所需要的方法。
叶子节点实现了符合中的具体行为,因为它们是容器中的元素。

Implementing the Composite Menu

从P400的代码来看MenuComponent是用户直接接触的类型。
所有的Menu都是MenuComponent的子类,Menu本身也是各种Menu和MenuItem的Menu。
这里所说的print由于是在一颗树形结构上,所以某一节点还需要把它的父结点路径上的所有print都调用一遍才能打印出完整的print内容,方法就是调用各自的iterator。
这样一来waitress就能很方便地整棵树的内容,而且代码极大简化,如P402。
对于客户而言,只需要把new出来的MenuComponent的对象加到Waitress对象里面就OK了。
客户对于叶子结点和中间结点是没有概念的,是完全没有意识的。
composite pattern会处理中间结点和叶子结点,这好像有点违背一个类只能有一个功能的原则,但实际上由于对中间结点和叶子结点的处理方式相同,所以composite pattern也不算是一个类有二个以上的功能了。

如何将iterator融入composite pattern中?

首先通过在MenuComponent中添加createIterator方法,这样无论是Menu和MenuItem都可以。
遍历整个二叉树的迭代器是另外继承iterator类而来的CompositeIterator,它创建iterator,取下一个结点和判断下一个结点的方法都用到了栈。

The Null Iterator

因为一个菜单项并不是集合,所以没法用iterator遍历它,但是为了让MenuComponent处理中间结点和叶子结点的时候方式一致,书中采用了两种方式来处理iterator创建函数。
我们的目的是要告诉客户已经没有可供遍历的项了,所以要么在iterator创建函数中返回null,要么返回一个专门表示没有项的iterator。
很显然后者较为合理,因为前者是Java语法层面的没有,它可以代表很多方面的没有,从业务逻辑的角度讲你无法做到一眼看出它代表什么意义。而后者是业务逻辑层面上的没有,它代表转移的意义,不会让你瞎想。后者就被称为NullIterator。
try-catch块负责错误处理与程序逻辑无关,使用它可以避免原有代码的修改,也有利于子类同名方法覆盖。

复合模式总结

它适用于由大量对象组成的不同集合,而且这些集合是以总分的方式组织在一起的,并且用户想统一管理所有对象。
所有属性结点都应该实现统一的接口,这样用户面对的就是统一的类型。
树形结构中的结点都有指向父结点的指针,这会让删除子结点更加方便。
它大大简化了客户管理大量不同集合对象的过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,897评论 1 15
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,483评论 0 3
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 我叫李璇,如今上了高中的我,很怀念初中的自己,怀念我那613宿舍的每一位人。 她,是我们宿舍的寝室长...
    fighting璇阅读 225评论 0 1