第二十七章 老公,有钱不?——迭代器模式
27.1 老公,有钱不?
时间:1月12日 地点:大B房间 人物:大B,小A
大B:“现在的社会都是男人挣钱女人花,比如说有个人,他有很多钱,钱对来他说就是一个聚合对象,他老婆是个会花钱的主。”
小A:“有钱嘛!怕什么?”
大B:“是啊。她老婆不断地从她老公那取钱出来花,每次要钱,总是先问:老公,有钱不?”
小A:“呵呵!那她老公怎么说?”
大B:“她老公就会说:有!”
小A:“那不错嘛!”
大B:“是啊。接着她老婆就会说‘拿N块钱出来我要买新衣服!’”
小A:“嘿嘿!那她老公会给她钱吗?”
大B:“会,她老公说:‘好,给!’”
小A:“嘿嘿!那还真不错喔!”
大B:“但是渐渐的,他老公觉得嫌烦,整天忙着赚钱(一个责任),累得要死,现在连要钱也得烦他 (另一个责任)!”
小A:“是喔!这样久了是挺烦人的。”
大B:“他老婆也觉得累,因为每次她老公的钱都不是放在同一地方,一会放钱包里,一会放上衣口袋,一会放裤子口袋,都得自己去翻,很麻烦的!”
小A:“那后来怎么办呢?”
大B:“遇到这个问题,她老公想到一个好方法,他把钱放进银行,办了一张银行卡,这样每次他老婆要钱就把银行卡给他,不要再回答她那么多问题了,她老婆也方便,不用操心钱放口袋还是钱包,只要到银行的自动取钱机取就OK了!”
小A:“嗯,这样就方便多了。”
27.2 迭代器模式
这个模式就是一个迭代器模式的生活例子!对于赚钱的老公,他就是一个聚合类,钱对他来说就是一个聚合对象,他老婆就是一个客户端应用程序,银行卡就是一个迭代器!将检查是否有钱和取钱的功能分离给银行卡完成!这样他可以安心去挣钱了!银行卡完成了一个迭代器的功能,有检查是否有钱和取钱的功能!
大B:“好了,说这么多,我们对迭代器模式有了个大概了解!”
小A:“在面向对象的软件设计中,我们经常会遇到一类集合对象,这类集合对象的内部结构可能有着各种各样的实现。”
大B:“归结起来,无非有两点是需要我们去关心的:一是集合内部的数据存储结构,二是遍历集合内部的数据。面向对象设计原则中有一条是类的单一职责原则,所以我们要尽可能的去分解这些职责,用不同的类去承担不同的职责。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。”
27.3 迭代器模式的角色
大B:“迭代器模式有哪些角色?”(www.xing528.com)
小A:“1、迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。2、具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。3、容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。4、具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。”
迭代器模式的类图27-1如下:
图27-1 迭代器模式的类图
小A:“从结构上,迭代器模式在客户与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符号‘单一职责原则’。”
大B:“注意,在迭代器模式中,具体迭代器角色和具体容器角色是耦合在一起的——遍历算法是与容器的内部细节紧密相关的。为了使客户程序从与具体迭代器角色耦合的困境中脱离出来,避免具体迭代器角色的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器角色,使得客户程序更具一般性和重用性。这被称为多态迭代。”
27.4 适用情况
小A:“迭代器模式给容器的应用会带来什么好处?”
大B:“1、支持以不同的方式遍历一个容器角色。根据实现方式的不同,效果上会有差别。2、简化了容器的接口。但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。3、对同一个容器对象,可以同时进行多个遍历。因为遍历状态是保存在每一个迭代器对象中的。这样就得出迭代器模式的适用范围:1、访问一个容器对象的内容而无需暴露它的内部表示。2、支持对容器对象的多种遍历。3、为遍历不同的容器结构提供一个统一的接口(多态迭代)。”
27.5 实现自己的迭代器
大B:“在实现自己的迭代器的时候,一般要操作的容器有支持的接口才可以。”
小A:“喔。”
大B:“而且我们还要注意以下问题:在迭代器遍历的过程中,通过该迭代器进行容器元素的增减操作是否安全呢?在容器中存在复合对象的情况,迭代器怎样才能支持深层遍历和多种遍历呢?以上两个问题对于不同结构的容器角色,各不相同,值得考虑。”
27.6 用Iterator模式实现遍历集合
小A:“怎样用Iterator模式实现遍历集合?”
大B:“Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。例如,如果没有使用Iterator,遍历一个数组的方法是使用索引: for(int i=0; i
小A:“那我们应该怎样去解决这此问题哩?”
大B:“为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合:for(Iterator it = c.iterater(); it.hasNext(); ) { ... }奥秘在于客户端自身不维护遍历集合的‘指针’,所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。客户端从不直接和集合类打交道,它总是控制Iterator,向它发送‘向前’,‘向后’,‘取当前元素’的命令,就可以间接遍历整个集合。这样看来实现Iterator的目的是降低耦合以及实现统一的遍历模式吧。在JS里面,遍历数组和遍历Object是不一样的,一般数组是 for(i=0; i
27.7 迭代器模式的实现方式
大B:“由于迭代器模式本身的规定比较松散,所以具体实现也就五花八门。”
小A:“我们又应该用什么方法去实现迭代器模式?”
大B:“1、迭代器角色定义了遍历的接口,但是没有规定由谁来控制迭代。在Java collection的应用中,是由客户程序来控制遍历的进程,被称为外部迭代器;还有一种实现方式便是由迭代器自身来控制迭代,被称为内部迭代器。外部迭代器要比内部迭代器灵活、强大,而且内部迭代器在Java语言环境中,可用性很弱。2、在迭代器模式中没有规定谁来实现遍历算法。好像理所当然的要在迭代器角色中实现。因为既便于一个容器上使用不同的遍历算法,也便于将一种遍历算法应用于不同的容器。但是这样就破坏掉了容器的封装——容器角色就要公开自己的私有属性,在Java中便意味着向其他类公开了自己的私有属性。”
小A:“那我们把它放到容器角色里来实现好了。这样迭代器角色就被架空为仅仅存放一个遍历当前位置的功能。但是遍历算法便和特定的容器紧紧绑在一起了。”
大B:“而在Java Collection的应用中,提供的具体迭代器角色是定义在容器角色中的内部类。这样便保护了容器的封装。但是同时容器也提供了遍历算法接口,你可以扩展自己的迭代器。好了,我们来看下Java Collection中的迭代器是怎么实现的吧。”
大B:“至于迭代器模式的使用。如引言中所列那样,客户程序要先得到具体容器角色,然后再通过具体容器角色得到具体迭代器角色。这样便可以使用具体迭代器角色来遍历容器了。”
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。