迭代器模式(Iterator Pattern)
概念
迭代器模式是一种行为设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式将数据的遍历操作与数据的存储结构分离,使得遍历算法可以独立于数据结构的变化。
迭代器模式的核心思想是将聚合对象的遍历操作抽象成迭代器接口,不同的聚合对象可以创建不同的迭代器实现,客户端只需要通过迭代器接口就能遍历聚合对象,而不需要了解聚合对象的内部结构。
基本实现
迭代器模式包含以下主要角色:
- Iterator(迭代器接口):定义访问和遍历元素的接口。
- ConcreteIterator(具体迭代器):实现迭代器接口,跟踪当前遍历的位置。
- Aggregate(聚合接口):定义创建相应迭代器对象的接口。
- 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()));关键要点
解耦遍历算法和数据结构:迭代器模式将遍历算法与数据结构分离,使得相同的遍历算法可以用于不同的数据结构。
支持多种遍历方式:同一个聚合对象可以有多种遍历方式,如正向、反向、按条件过滤等。
简化聚合类接口:聚合类不需要实现复杂的遍历接口,只需要提供创建迭代器的方法。
多态迭代:不同的聚合对象可以返回不同类型的迭代器,客户端可以统一使用迭代器接口。
与其他模式的关系
- 与组合模式结合:可以在组合模式的树形结构上使用迭代器模式进行遍历。
- 与工厂方法模式结合:聚合类通过工厂方法创建具体的迭代器对象。
- 与备忘录模式结合:迭代器可以使用备忘录来保存遍历状态,实现断点续遍历。
优缺点
优点:
- 简化聚合类:聚合类只需要提供创建迭代器的方法,不需要了解遍历算法的细节。
- 支持多种遍历方式:同一个聚合对象可以有多种遍历方式,增加灵活性。
- 扩展性好:增加新的聚合类和迭代器类都很方便,符合开闭原则。
- 解耦遍历算法和数据结构:遍历算法的变化不会影响数据结构,反之亦然。
缺点:
- 增加类的数量:每个聚合类都需要一个对应的迭代器类,增加系统复杂性。
- 可能浪费资源:对于简单的聚合对象,使用迭代器模式可能显得过于复杂。
迭代器模式在现代编程语言中已经非常普遍,大多数语言都内置了迭代器的支持,如JavaScript的for...of循环、Python的迭代器协议等。理解迭代器模式有助于更好地使用这些语言特性,并在需要时自定义迭代逻辑。