迭代器模式

61 阅读5分钟

1. 简介

迭代器模式是一种行为设计模式, 让你能够在不暴露集合底层数据结构形式(列表、栈、队列、树和图等)的情况下遍历集合中所有的元素。

2. 迭代器模式的结构

classDiagram
    
    class Client {
        +client()
    }
    
    class Iterable {
        <<iterface>>
        +__iter__(): Iterator
    }
    
    class ConcreteIterable {
        -collection: List[Any]
        +add(element: Any)
        +__iter__(): Iterator
    }
    
    class Iterator {
        <<interface>>
        +__iter__(): Iterator
        +__next__(): Any
    }
    
    class ConcreteIterator {
        -collection: List[Any]
        -index: int
        +__iter__(): ConcreteIterator
        +__next__(): Any
    }
    
    Iterable <|.. ConcreteIterable
    Iterable ..> Iterator
    Iterator <|.. ConcreteIterator
    Client --> Iterable
    Client --> Iterator
  1. 迭代器(Iterator)接口声明了遍历集合所需的操作:获取下一个元素、获取当前位置和重新开始迭代等。

  2. 具体迭代器(Concrete Iterators)实现遍历集合的一种特定算法。迭代器对象必须跟踪自身遍历的进度。这使得多个迭代器可以相互独立地遍历同一集合。

  3. 可迭代的集合(Iterable)接口声明一个或多个方法来获取与集合兼容的迭代器。请注意,返回方法的类型必须被声明为迭代器接口,因此具体集合可以返回各种不同种类的迭代器。

  4. 具体集合(Concrete Iterable)会在客户端请求迭代器时返回一个特定的具体迭代器类实体。你可能会琢磨,剩下的集合代码在什么地方呢?不用担心,它也会在同一个类中。只是这些细节对于实际模式来说并不重要,所以我们将其省略了而已。

  5. 客户端(Client)通过集合和迭代器的接口与两者进行交互。这样一来客户端无需与具体类进行耦合,允许同一客户端代码使用各种不同的集合和迭代器。客户端通常不会自行创建迭代器,而是会从集合中获取。但在特定情况下,客户端可以直接创建一个迭代器(例如当客户端需要自定义特殊迭代器时)。

当然,在简单的应用场景下,迭代器接口和可迭代的集合接口往往可以省略。

3. 迭代器模式的优缺点以及使用场景

优点:

  1. 简化遍历操作:迭代器模式提供了一种统一的遍历接口,使得客户端无需关心集合的内部数据结构,可以简单地遍历集合中的元素。
  2. 隔离集合与遍历逻辑:迭代器模式将集合的遍历逻辑封装在迭代器中,使得集合和遍历逻辑能够独立变化,从而提高了代码的灵活性和可维护性。
  3. 支持多种遍历方式:迭代器模式可以支持多种遍历方式,如顺序遍历、逆序遍历、按条件遍历等,对树或者图的集合形式,还可以自定义深度优先遍历、宽度优先遍历,因此,客户端可以根据需要选择合适的遍历方式。
  4. 简化客户端代码:使用迭代器模式可以简化客户端的代码,使得客户端只需关注业务逻辑,而无需关心集合的遍历细节。

缺点:

  1. 增加类和对象的数量:迭代器模式会引入额外的迭代器类和迭代器对象,可能会增加系统的复杂性。
  2. 不适合所有集合:迭代器模式适用于遍历访问聚合对象中的元素,但并不是所有集合都适合使用迭代器模式,特别是对于一些固定大小或结构简单的集合,使用迭代器可能会显得繁琐。

使用场景:

  1. 需要统一遍历接口:当集合的遍历逻辑复杂或需要多种遍历方式时,可以使用迭代器模式统一遍历接口,简化客户端的代码。
  2. 需要隔离集合与遍历逻辑:当集合的内部结构可能经常变化,或者需要对集合和遍历逻辑进行独立扩展时,可以使用迭代器模式隔离集合与遍历逻辑,提高代码的灵活性和可维护性。
  3. 需要支持多种遍历方式:当需要支持多种遍历方式,如顺序遍历、逆序遍历、按条件遍历等时,可以使用迭代器模式灵活地选择合适的遍历方式。
  4. 需要简化客户端代码:当客户端需要遍历访问集合中的元素,但不想关心集合的内部结构和遍历细节时,可以使用迭代器模式简化客户端的代码。

4. 练习:【设计模式专题之迭代器模式】19-学生名单

"""  
【设计模式专题之迭代器模式】19-学生名单
时间限制:1.000S  空间限制:256MB
题目描述
小明是一位老师,在进行班级点名时,希望有一个学生名单系统,请你实现迭代器模式提供一个迭代器使得可以按顺序遍历学生列表。
输入描述
第一行是一个整数 N (1 <= N <= 100), 表示学生的数量。

接下来的 N 行,每行包含一个学生的信息,格式为 姓名 学号

输出描述
输出班级点名的结果,即按顺序遍历学生列表,输出学生的姓名和学号
输入示例
3
Alice 1001
Bob 1002
Charlie 1003
输出示例
Alice 1001
Bob 1002
Charlie 1003
"""
from typing import List


class Student:
    def __init__(self, name: str, number: str):
        self.name = name
        self.number = number
    
    def attend(self):
        print(f"{self.name} {self.number}")
        
        
class IterableStudents:
    def __init__(self):
        self.students: List[Student] = []
        
    def add(self, student: Student):
        self.students.append(student)
        
    def __iter__(self):
        return StudentsIterator(self.students)
        
        
class StudentsIterator:
    def __init__(self, students: List[Student]):
        self.students = students
        self.index = 0
        
    def __iter__(self):
        return self
        
    def __next__(self):
        try:
            student = self.students[self.index]
        except IndexError:
            raise StopIteration
        else:
            self.index += 1
            return student
            

def client():
    students = IterableStudents()
    n = int(input())
    for _ in range(n):
        name, number = input().split()
        student = Student(name, number)
        students.add(student)
    iterator = iter(students)
    while True:
        try:
            student = next(iterator)
        except StopIteration:
            break
        else:
            student.attend()
            
            
if __name__ == "__main__":
    client()

5. 参考文章