设计模式-迭代器模式

我们两清 提交于 2020-03-17 08:10:32

某厂面试归来,发现自己落伍了!>>>

1. 迭代器模式概述

迭代器模式, 我觉得这个图还是很贴切的. 迭代器相当于是电视机的遥控器, 聚合对象相当于是电视机. 电视机中有很多电视频道的集合, 那些电视频道到底是怎么组合在一起的, 我们并不知道, 也无需知道, 因为遥控器也就是迭代器给我们提供了遍历电视频道的方法, 我们只需要调用不同的迭代器方法即可遍历电视机中所有节目的集合.

在软件开发中, 我们经常需要使用聚合对象来存储一系列的数据. 聚合对象有两个职责: 存储数据; 遍历数据. 从依赖性来看, 前者是聚合对象的基本职责; 而后者既是可变的, 也是可分离的. 因此可将遍历数据的行为从聚合对象中分离出来, 封装在一个被称为"迭代器"的对象中, 由迭代器来提供遍历局和对象内部数据的行为, 这将简化聚合对象的设计, 更符合"单一职责原则"的要求.

迭代器模式: 提供一种方法来访问聚合对象, 而不用暴露这个对象的内部表示, 其别名为 游标. 迭代器模式是一种对象行为型模式.

在迭代器模式中包含聚合和迭代器两个层次结构, 考虑到系统的灵活性和可扩展性, 在迭代器模式中应用了工厂方法模式, 模式结构图如下:

迭代器模式中的几个角色:

  • 抽象迭代器(Iterator): 它定义了访问和遍历元素的接口, 声明了用于遍历数据元素的方法.
  • 具体迭代器(ConcreteIterator): 它实现了抽象迭代器接口, 完成对聚合对象的遍历, 同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置, 在聚合实现时, 游标通常是一个表示位置的非负整数.
  • 抽象聚合类(Aggregate): 用于存储和管理元素对象, 声明一个createIterator()方法, 该方法用于创建一个具体聚合类对象, 充当抽象迭代器的工厂角色.
  • 聚合聚合类(ConcreteAggregate): 它实现了在抽象聚合类中声明的createIterator()方法, 该方法返回一个与该具体聚合类对应的聚合迭代器ConcreateIterator实例.

在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。

2. Swift实现迭代器模式

一个简单的迭代器模式的Swift实现

抽象迭代器  AbsIterator 

// 抽象迭代器, 用接口来声明
protocol AbsIterator {
    
    func next()
    func isLast() -> Bool
    func previous()
    func isFirst() -> Bool
    func getNextItem() -> AnyObject
    func getPreviousItem() -> AnyObject
    
}

具体迭代器 ContreateIterator

class ContreateIterator: NSObject, AbsIterator {

    var productList: ProductList
    var list: [AnyObject]
    
    var cursor1: Int
    var cursor2: Int
    
    init(productList: ProductList) {
        
        self.productList = productList
        list = productList.objects
        cursor1 = 0
        cursor2 = list.count
        
    }
    
    func next() {
        cursor1 += 1
    }
    func isLast() -> Bool {
        return cursor1 == list.count
    }
    func previous() {
        cursor2 -= 1
    }
    func isFirst() -> Bool {
        return cursor2 == 0
    }
    func getNextItem() -> AnyObject {
        return list[cursor1]
    }
    func getPreviousItem() -> AnyObject {
        return list[cursor2]
    }
    
}

抽象聚合类 AbsObjectList

class AbsObjectList: NSObject {
    
    var objects: [AnyObject]
    
    init(objs: [AnyObject]) {
        
        objects = objs
        
    }
    
    func getObjects() -> [AnyObject] {
        return objects;
    }
    
    func addObject(obj: AnyObject) {
        objects.append(obj)
    }
    
    func removeObject(obj: AnyObject) {
        
    }
    
    func createIterator() -> AbsIterator {
        return Optional.none!
    }
}

具体聚合类 ProductList

class ProductList: AbsObjectList {
    
    override init(objs: [AnyObject]) {
        super.init(objs: objs)
    }
    
    override func createIterator() -> AbsIterator {
        return ContreateIterator(productList: self)
    }
}





客户端代码:

var array = [String]()

array.append("walden")
array.append("wei")
array.append("shengli")
array.append("liuyong")
array.append("jinlong")

let products = ProductList(objs: array as [AnyObject])

let iterator = products.createIterator()

print("\(iterator.getNextItem())")

如果在如上所示代码中增加新的具体聚合类, 只需增加新的聚合类子类和一个新的具体迭代器类, 原有类库代码无需修改, 符合开闭原则; 如果需要更换迭代器, 只需要增加一个新的具体迭代器作为抽象迭代器的子类, 重新实现遍历方法, 符合开闭原则; 但是如果要在迭代器中增加新的方法, 则需要修改抽象迭代器源代码, 这将违背开闭原则.

在迭代器模式的结构图中, 我们可以看到具体迭代器类和具体聚合类之间存在双重关系, 其中一个关系为关联关系, 在具体迭代器中需要维持一个队具体聚合对象的引用, 该关联关系的目的是访问存储在聚合对象中的数据, 以便迭代器能够对这些数据进行遍历操作. 除了使用关联关系, 为了能够让迭代器可以访问到聚合对象中的数据, 我们可以将迭代器设计为聚合类的内部类, 在java的JDK中, 就是通过这种方式实现的.

3. 总结

迭代器模式是一种使用频率非常高的设计模式,通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据由迭代器来完成。由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,我们只需要直接使用Java、C#等语言已定义好的迭代器即可,迭代器已经成为我们操作聚合对象的基本工具之一。

3.1 主要优点

迭代器模式的主要优点如下:

  1. 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
  2. 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
  3. 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。

2. 主要缺点

迭代器模式的主要缺点如下:

  1. 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
  2. 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

3. 适用场景

在以下情况下可以考虑使用迭代器模式:

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
  2. 需要为一个聚合对象提供多种遍历方式。
  3. 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。

更多详情请参考原文:

Reference: http://blog.csdn.net/lovelion/article/details/9992005

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!