Skip to content

迭代器模式(Iterator Pattern)

概念

迭代器模式是一种行为设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式将数据的遍历操作与数据的存储结构分离,使得遍历算法可以独立于数据结构的变化。

迭代器模式的核心思想是将聚合对象的遍历操作抽象成迭代器接口,不同的聚合对象可以创建不同的迭代器实现,客户端只需要通过迭代器接口就能遍历聚合对象,而不需要了解聚合对象的内部结构。

基本实现

迭代器模式包含以下主要角色:

  1. Iterator(迭代器接口):定义访问和遍历元素的接口。
  2. ConcreteIterator(具体迭代器):实现迭代器接口,跟踪当前遍历的位置。
  3. Aggregate(聚合接口):定义创建相应迭代器对象的接口。
  4. ConcreteAggregate(具体聚合):实现聚合接口,返回一个具体迭代器实例。

下面是迭代器模式的基本实现:

javascript
// 迭代器接口
class Iterator {
  hasNext() {
    throw new Error('hasNext method must be implemented');
  }

  next() {
    throw new Error('next method must be implemented');
  }
}

// 聚合接口
class Aggregate {
  createIterator() {
    throw new Error('createIterator method must be implemented');
  }
}

// 具体聚合 - 数组列表
class ArrayList extends Aggregate {
  constructor() {
    super();
    this.items = [];
  }

  add(item) {
    this.items.push(item);
  }

  get(index) {
    return this.items[index];
  }

  size() {
    return this.items.length;
  }

  createIterator() {
    return new ArrayListIterator(this);
  }
}

// 具体迭代器
class ArrayListIterator extends Iterator {
  constructor(arrayList) {
    super();
    this.arrayList = arrayList;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.arrayList.size();
  }

  next() {
    if (this.hasNext()) {
      return this.arrayList.get(this.index++);
    }
    return null;
  }
}

// 使用示例
const list = new ArrayList();
list.add('Apple');
list.add('Banana');
list.add('Cherry');

const iterator = list.createIterator();
while (iterator.hasNext()) {
  console.log(iterator.next());
}

实际应用场景

场景1:浏览器DOM元素遍历

在浏览器中,DOM元素的遍历就是一个典型的应用迭代器模式的场景,可以按不同方式遍历DOM树。

javascript
// DOM元素迭代器接口
class DOMIterator {
  hasNext() {}
  next() {}
  reset() {}
}

// 深度优先遍历迭代器
class DepthFirstIterator extends DOMIterator {
  constructor(rootElement) {
    super();
    this.stack = [rootElement];
    this.current = null;
  }

  hasNext() {
    return this.stack.length > 0;
  }

  next() {
    if (!this.hasNext()) return null;
    
    this.current = this.stack.pop();
    
    // 将子元素按逆序加入栈中,保证左到右的顺序
    const children = this.current.children;
    for (let i = children.length - 1; i >= 0; i--) {
      this.stack.push(children[i]);
    }
    
    return this.current;
  }

  reset(rootElement) {
    this.stack = [rootElement];
    this.current = null;
  }
}

// 广度优先遍历迭代器
class BreadthFirstIterator extends DOMIterator {
  constructor(rootElement) {
    super();
    this.queue = [rootElement];
    this.current = null;
  }

  hasNext() {
    return this.queue.length > 0;
  }

  next() {
    if (!this.hasNext()) return null;
    
    this.current = this.queue.shift();
    
    // 将子元素加入队列末尾
    const children = this.current.children;
    for (let i = 0; i < children.length; i++) {
      this.queue.push(children[i]);
    }
    
    return this.current;
  }

  reset(rootElement) {
    this.queue = [rootElement];
    this.current = null;
  }
}

// 元素选择器迭代器
class ElementSelectorIterator extends DOMIterator {
  constructor(rootElement, selector) {
    super();
    this.elements = Array.from(rootElement.querySelectorAll(selector));
    this.index = 0;
    this.current = null;
  }

  hasNext() {
    return this.index < this.elements.length;
  }

  next() {
    if (!this.hasNext()) return null;
    
    this.current = this.elements[this.index++];
    return this.current;
  }

  reset() {
    this.index = 0;
    this.current = null;
  }
}

// 使用示例(模拟DOM元素)
class MockElement {
  constructor(tagName, id = null) {
    this.tagName = tagName;
    this.id = id;
    this.children = [];
    this.parent = null;
  }

  appendChild(child) {
    child.parent = this;
    this.children.push(child);
  }

  toString() {
    return `<${this.tagName}${this.id ? ' id="' + this.id + '"' : ''}>`;
  }
}

// 创建模拟DOM树
const root = new MockElement('div', 'root');
const child1 = new MockElement('p', 'p1');
const child2 = new MockElement('div', 'div1');
const child3 = new MockElement('span', 'span1');
const grandChild1 = new MockElement('a', 'link1');
const grandChild2 = new MockElement('img');

root.appendChild(child1);
root.appendChild(child2);
root.appendChild(child3);
child2.appendChild(grandChild1);
child2.appendChild(grandChild2);

console.log('=== 深度优先遍历 ===');
let dfsIterator = new DepthFirstIterator(root);
while (dfsIterator.hasNext()) {
  const element = dfsIterator.next();
  console.log(element.toString());
}

console.log('\n=== 广度优先遍历 ===');
let bfsIterator = new BreadthFirstIterator(root);
while (bfsIterator.hasNext()) {
  const element = bfsIterator.next();
  console.log(element.toString());
}

console.log('\n=== 选择器遍历(div元素) ===');
let selectorIterator = new ElementSelectorIterator(root, 'div');
while (selectorIterator.hasNext()) {
  const element = selectorIterator.next();
  console.log(element.toString());
}

场景2:数据集合的多种遍历方式

在数据处理系统中,同一个数据集合可能需要多种遍历方式,如正向遍历、反向遍历、跳跃遍历等。

javascript
// 抽象聚合接口
class Collection {
  createIterator() {}
  createReverseIterator() {}
  createStepIterator(step) {}
  add(item) {}
  get(index) {}
  size() {}
}

// 具体聚合 - 自定义列表
class CustomList extends Collection {
  constructor() {
    super();
    this.items = [];
  }

  add(item) {
    this.items.push(item);
  }

  get(index) {
    if (index >= 0 && index < this.items.length) {
      return this.items[index];
    }
    return undefined;
  }

  size() {
    return this.items.length;
  }

  createIterator() {
    return new ForwardIterator(this);
  }

  createReverseIterator() {
    return new ReverseIterator(this);
  }

  createStepIterator(step) {
    return new StepIterator(this, step);
  }
}

// 正向迭代器
class ForwardIterator extends Iterator {
  constructor(list) {
    super();
    this.list = list;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.list.size();
  }

  next() {
    if (this.hasNext()) {
      return this.list.get(this.index++);
    }
    return null;
  }

  reset() {
    this.index = 0;
  }
}

// 反向迭代器
class ReverseIterator extends Iterator {
  constructor(list) {
    super();
    this.list = list;
    this.index = this.list.size() - 1;
  }

  hasNext() {
    return this.index >= 0;
  }

  next() {
    if (this.hasNext()) {
      return this.list.get(this.index--);
    }
    return null;
  }

  reset() {
    this.index = this.list.size() - 1;
  }
}

// 步长迭代器
class StepIterator extends Iterator {
  constructor(list, step) {
    super();
    this.list = list;
    this.step = step;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.list.size();
  }

  next() {
    if (this.hasNext()) {
      const item = this.list.get(this.index);
      this.index += this.step;
      return item;
    }
    return null;
  }

  reset() {
    this.index = 0;
  }
}

// 条件迭代器
class FilterIterator extends Iterator {
  constructor(iterator, predicate) {
    super();
    this.iterator = iterator;
    this.predicate = predicate;
    this.nextItem = null;
    this.findNext();
  }

  findNext() {
    while (this.iterator.hasNext()) {
      const item = this.iterator.next();
      if (this.predicate(item)) {
        this.nextItem = item;
        return;
      }
    }
    this.nextItem = null;
  }

  hasNext() {
    return this.nextItem !== null;
  }

  next() {
    if (!this.hasNext()) return null;
    
    const item = this.nextItem;
    this.findNext();
    return item;
  }
}

// 使用示例
const list = new CustomList();
for (let i = 1; i <= 10; i++) {
  list.add(`Item ${i}`);
}

console.log('=== 正向遍历 ===');
let forwardIterator = list.createIterator();
while (forwardIterator.hasNext()) {
  console.log(forwardIterator.next());
}

console.log('\n=== 反向遍历 ===');
let reverseIterator = list.createReverseIterator();
while (reverseIterator.hasNext()) {
  console.log(reverseIterator.next());
}

console.log('\n=== 步长为2的遍历 ===');
let stepIterator = list.createStepIterator(2);
while (stepIterator.hasNext()) {
  console.log(stepIterator.next());
}

console.log('\n=== 过滤遍历(只显示偶数项) ===');
let filterIterator = new FilterIterator(
  list.createIterator(), 
  item => parseInt(item.split(' ')[1]) % 2 === 0
);
while (filterIterator.hasNext()) {
  console.log(filterIterator.next());
}

场景3:数据库查询结果集遍历

在数据库访问层中,查询结果集通常通过迭代器模式来遍历,这样可以按需获取数据,避免一次性加载大量数据。

javascript
// 模拟数据库记录
class DatabaseRecord {
  constructor(id, name, email, age) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.age = age;
  }

  toString() {
    return `User{id=${this.id}, name='${this.name}', email='${this.email}', age=${this.age}}`;
  }
}

// 模拟数据库连接
class MockDatabase {
  constructor() {
    // 模拟数据
    this.data = [
      new DatabaseRecord(1, 'Alice', 'alice@example.com', 25),
      new DatabaseRecord(2, 'Bob', 'bob@example.com', 30),
      new DatabaseRecord(3, 'Charlie', 'charlie@example.com', 35),
      new DatabaseRecord(4, 'David', 'david@example.com', 28),
      new DatabaseRecord(5, 'Eve', 'eve@example.com', 22),
      new DatabaseRecord(6, 'Frank', 'frank@example.com', 40),
      new DatabaseRecord(7, 'Grace', 'grace@example.com', 33),
      new DatabaseRecord(8, 'Henry', 'henry@example.com', 29),
      new DatabaseRecord(9, 'Ivy', 'ivy@example.com', 27),
      new DatabaseRecord(10, 'Jack', 'jack@example.com', 31)
    ];
  }

  query(sql) {
    // 简单模拟SQL查询
    if (sql.includes('SELECT')) {
      return new ResultSet(this.data);
    }
    return new ResultSet([]);
  }
}

// 结果集接口
class ResultSet {
  createIterator() {}
  createLimitIterator(limit) {}
  createPageIterator(pageSize) {}
}

// 具体结果集
class DatabaseResultSet extends ResultSet {
  constructor(records) {
    super();
    this.records = records;
  }

  createIterator() {
    return new ResultSetIterator(this.records);
  }

  createLimitIterator(limit) {
    return new LimitIterator(this.records, limit);
  }

  createPageIterator(pageSize) {
    return new PageIterator(this.records, pageSize);
  }

  size() {
    return this.records.length;
  }
}

// 基本结果集迭代器
class ResultSetIterator extends Iterator {
  constructor(records) {
    super();
    this.records = records;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.records.length;
  }

  next() {
    if (this.hasNext()) {
      return this.records[this.index++];
    }
    return null;
  }

  reset() {
    this.index = 0;
  }
}

// 限制数量迭代器
class LimitIterator extends Iterator {
  constructor(records, limit) {
    super();
    this.records = records;
    this.limit = limit;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.records.length && this.index < this.limit;
  }

  next() {
    if (this.hasNext()) {
      return this.records[this.index++];
    }
    return null;
  }

  reset() {
    this.index = 0;
  }
}

// 分页迭代器
class PageIterator {
  constructor(records, pageSize) {
    this.records = records;
    this.pageSize = pageSize;
    this.currentPage = 0;
  }

  hasNextPage() {
    return (this.currentPage + 1) * this.pageSize < this.records.length;
  }

  nextPage() {
    if (!this.hasNextPage()) return [];

    this.currentPage++;
    const start = this.currentPage * this.pageSize;
    const end = Math.min(start + this.pageSize, this.records.length);
    return this.records.slice(start, end);
  }

  previousPage() {
    if (this.currentPage <= 0) return [];

    this.currentPage--;
    const start = this.currentPage * this.pageSize;
    const end = start + this.pageSize;
    return this.records.slice(start, end);
  }

  firstPage() {
    this.currentPage = 0;
    const end = Math.min(this.pageSize, this.records.length);
    return this.records.slice(0, end);
  }

  getCurrentPage() {
    const start = this.currentPage * this.pageSize;
    const end = Math.min(start + this.pageSize, this.records.length);
    return this.records.slice(start, end);
  }

  getTotalPages() {
    return Math.ceil(this.records.length / this.pageSize);
  }
}

// 使用示例
const db = new MockDatabase();
const resultSet = db.query('SELECT * FROM users');

console.log('=== 基本遍历 ===');
let iterator = resultSet.createIterator();
while (iterator.hasNext()) {
  console.log(iterator.next().toString());
}

console.log('\n=== 限制前5条记录 ===');
let limitIterator = resultSet.createLimitIterator(5);
while (limitIterator.hasNext()) {
  console.log(limitIterator.next().toString());
}

console.log('\n=== 分页遍历 (每页3条) ===');
let pageIterator = resultSet.createPageIterator(3);
console.log(`总页数: ${pageIterator.getTotalPages()}`);

console.log('\n第1页:');
pageIterator.firstPage().forEach(record => console.log(record.toString()));

console.log('\n第2页:');
pageIterator.nextPage().forEach(record => console.log(record.toString()));

console.log('\n第3页:');
pageIterator.nextPage().forEach(record => console.log(record.toString()));

console.log('\n回到第2页:');
pageIterator.previousPage().forEach(record => console.log(record.toString()));

关键要点

  1. 解耦遍历算法和数据结构:迭代器模式将遍历算法与数据结构分离,使得相同的遍历算法可以用于不同的数据结构。

  2. 支持多种遍历方式:同一个聚合对象可以有多种遍历方式,如正向、反向、按条件过滤等。

  3. 简化聚合类接口:聚合类不需要实现复杂的遍历接口,只需要提供创建迭代器的方法。

  4. 多态迭代:不同的聚合对象可以返回不同类型的迭代器,客户端可以统一使用迭代器接口。

与其他模式的关系

  • 与组合模式结合:可以在组合模式的树形结构上使用迭代器模式进行遍历。
  • 与工厂方法模式结合:聚合类通过工厂方法创建具体的迭代器对象。
  • 与备忘录模式结合:迭代器可以使用备忘录来保存遍历状态,实现断点续遍历。

优缺点

优点:

  1. 简化聚合类:聚合类只需要提供创建迭代器的方法,不需要了解遍历算法的细节。
  2. 支持多种遍历方式:同一个聚合对象可以有多种遍历方式,增加灵活性。
  3. 扩展性好:增加新的聚合类和迭代器类都很方便,符合开闭原则。
  4. 解耦遍历算法和数据结构:遍历算法的变化不会影响数据结构,反之亦然。

缺点:

  1. 增加类的数量:每个聚合类都需要一个对应的迭代器类,增加系统复杂性。
  2. 可能浪费资源:对于简单的聚合对象,使用迭代器模式可能显得过于复杂。

迭代器模式在现代编程语言中已经非常普遍,大多数语言都内置了迭代器的支持,如JavaScript的for...of循环、Python的迭代器协议等。理解迭代器模式有助于更好地使用这些语言特性,并在需要时自定义迭代逻辑。