TypeScript设计模式:迭代器模式

迭代器模式(Iterator Pattern)是一种行为设计模式,用于提供一种方式来顺序访问聚合对象(如数组、列表或集合)的元素,而无需暴露其底层实现。

设计模式原理

什么是迭代器模式?

迭代器模式的核心思想是将集合的遍历逻辑从集合本身分离出来,交给一个独立的迭代器对象。这样可以实现:

  • 单一职责:集合负责存储数据,迭代器负责遍历逻辑。
  • 灵活性:支持多种遍历方式(如正序、倒序、过滤等)。
  • 封装性:隐藏集合的内部实现,客户端只需关注如何使用迭代器。

在 TypeScript 中,迭代器模式可以通过内置的 IterableIterator 接口实现,也可以通过自定义方式实现更复杂的逻辑。

迭代器模式的结构

迭代器模式通常包含以下几个角色:

  1. Iterator(迭代器接口) :定义遍历元素所需的方法,如 next()hasNext()
  2. ConcreteIterator(具体迭代器) :实现迭代器接口,负责具体的遍历逻辑。
  3. Aggregate(聚合接口) :定义创建迭代器的方法,如 createIterator()
  4. ConcreteAggregate(具体聚合) :实现聚合接口,存储数据并提供迭代器。

优点

  • 解耦合:将遍历逻辑与集合分离,降低耦合度。
  • 多态性:支持多种遍历方式,只需实现不同的迭代器。
  • 类型安全:TypeScript 的类型系统确保迭代器和集合的类型一致。

适用场景

  • 需要遍历复杂数据结构(如树、图)时。
  • 需要支持多种遍历方式(正序、倒序、过滤等)。
  • 希望隐藏集合的内部实现细节。

TypeScript 实现示例

以下是一个使用 TypeScript 实现迭代器模式的示例。我们将创建一个书架(Bookshelf)类,用于存储书籍,并通过迭代器遍历书籍列表。

typescript 复制代码
// 迭代器接口
interface Iterator<T> {
  hasNext(): boolean;
  next(): T | undefined;
}

// 聚合接口
interface Aggregate {
  createIterator(): Iterator<Book>;
}

// 书籍类
class Book {
  constructor(private title: string) {}

  getTitle(): string {
    return this.title;
  }
}

// 具体聚合类:书架
class Bookshelf implements Aggregate {
  private books: Book[] = [];

  addBook(book: Book): void {
    this.books.push(book);
  }

  getBooks(): Book[] {
    return this.books;
  }

  createIterator(): Iterator<Book> {
    return new BookshelfIterator(this);
  }
}

// 具体迭代器类
class BookshelfIterator implements Iterator<Book> {
  private bookshelf: Bookshelf;
  private index: number = 0;

  constructor(bookshelf: Bookshelf) {
    this.bookshelf = bookshelf;
  }

  hasNext(): boolean {
    return this.index < this.bookshelf.getBooks().length;
  }

  next(): Book | undefined {
    if (this.hasNext()) {
      return this.bookshelf.getBooks()[this.index++];
    }
    return undefined;
  }
}

// 客户端代码
function main() {
  // 创建书架
  const bookshelf = new Bookshelf();
  bookshelf.addBook(new Book("设计模式"));
  bookshelf.addBook(new Book("TypeScript 高级编程"));
  bookshelf.addBook(new Book("深入理解 ES6"));

  // 获取迭代器
  const iterator = bookshelf.createIterator();

  // 遍历书籍
  while (iterator.hasNext()) {
    const book = iterator.next();
    if (book) {
      console.log(`书籍标题: ${book.getTitle()}`);
    }
  }
}

main();

运行结果

运行以上代码,将输出:

makefile 复制代码
书籍标题: 设计模式
书籍标题: TypeScript 高级编程
书籍标题: 深入理解 ES6

使用 TypeScript 的内置迭代器

TypeScript 提供了内置的 IterableIterator 接口,可以简化迭代器模式的实现。例如,Bookshelf 类可以直接实现 Iterable<Book> 接口:

typescript 复制代码
class Bookshelf implements Iterable<Book> {
  private books: Book[] = [];

  addBook(book: Book): void {
    this.books.push(book);
  }

  [Symbol.iterator](): Iterator<Book> {
    let index = 0;
    const books = this.books;

    return {
      next(): IteratorResult<Book> {
        if (index < books.length) {
          return { value: books[index++], done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
}

// 客户端代码
function main() {
  const bookshelf = new Bookshelf();
  bookshelf.addBook(new Book("设计模式"));
  bookshelf.addBook(new Book("TypeScript 高级编程"));

  // 使用 for...of 遍历
  for (const book of bookshelf) {
    console.log(`书籍标题: ${book.getTitle()}`);
  }
}

main();

运行结果

makefile 复制代码
书籍标题: 设计模式
书籍标题: TypeScript 高级编程

JavaScript 内部迭代器原理

JavaScript 的迭代器协议(Iteration Protocol)是 ECMAScript 2015 (ES6) 引入的核心机制,用于定义对象如何被迭代遍历。 它包括同步迭代(Iterable 和 Iterator)和异步迭代(AsyncIterable 和 AsyncIterator),允许开发者自定义遍历行为。

同步迭代协议

Iterable 接口

任何对象如果实现了 Symbol.iterator 方法,则被视为可迭代对象。该方法返回一个符合 Iterator 协议的对象。当引擎遇到需要迭代的对象时(如 for...of),它会调用 [Symbol.iterator]() 获取迭代器。

Iterator 接口

迭代器对象必须实现 next() 方法,返回 { value: any, done: boolean }done 为 true 表示迭代结束。迭代器维护内部状态,每次 next() 调用推进状态。

语言集成

  • for...of :调用 [Symbol.iterator](),循环 next(),如果提前退出,调用 return() 清理资源。
  • 其他:Spread 操作符、Array.from()、解构赋值。
  • 内置对象:Array、Map、Set、String 等实现该协议.

生成器(function*yield)是迭代器的语法糖,返回 Generator 对象,既是 iterable 又是 iterator。

异步迭代协议

AsyncIterable 和 AsyncIterator

  • Symbol.asyncIterator:返回 AsyncIterator。
  • next():返回 Promise<IteratorResult>
  • for-await-of :异步版本的 for...of.

这适用于处理 Promise 或异步序列,如网络响应。

使用 Node.js 实现一个类似 Deno.serve 的方法

在 Web 服务器中,异步迭代器特别适合处理异步网络请求,例如逐个处理 incoming requests 或以流式方式发送响应数据,避免一次性加载整个数据到内存。基于对 Deno.serve 的分析(其利用 Deno.ListenerDeno.HttpConn 的 AsyncIterable 特性迭代连接和请求),我们实现一个 Node.js 版本的 serve 函数,类似于 Deno.serve,支持异步处理函数,并通过异步迭代器处理请求流和响应流。

实现示例

以下是一个精简的 Node.js HTTP 服务器实现,使用 TypeScript 和 async * 语法糖,隐藏显式 AsyncIterator 概念,清晰展示请求流到响应流的转换。

typescript 复制代码
import { createServer, IncomingMessage, ServerResponse } from 'http';
import { Readable } from 'stream';

// 自定义响应类型(类似于 Deno 的 Response)
type Handler = (req: IncomingMessage) => Promise<ResponseLike>;

interface ResponseLike {
  status?: number;
  headers?: Record<string, string>;
  body: string | Buffer | Readable | AsyncIterable<Buffer | string>;
}

// 将 request 事件转为 AsyncIterable
async function* listen(server: ReturnType<typeof createServer>) {
  const q: [IncomingMessage, ServerResponse][] = [];
  server.on('request', (req, res) => q.push([req, res]));

  while (true) {
    while (q.length) yield q.shift()!; // 先消化积压
    await new Promise(r => server.once('request', r)); // 再等下一个
  }
}

// 自定义 serve 函数,使用异步迭代处理请求
export async function serve(handler: Handler, port = 3000) {
  const server = createServer(); // 不挂任何回调,完全由我们控制
  server.listen(port, () => console.log(`http://localhost:${port}`));

  for await (const [req, res] of listen(server)) {
    try {
      const { status = 200, headers = {}, body } = await handler(req);

      res.writeHead(status, headers);
      if (Symbol.asyncIterator in body) {
        Readable.from(body as AsyncIterable<any>).pipe(res); // 流式输出
      } else {
        res.end(body); // 一次性输出
      }
    } catch {
      res.writeHead(500).end('Internal Server Error');
    }
  }
}

// 示例使用:异步 handler 处理请求
if (require.main === module) {
  serve(async req => {
    const { pathname } = new URL(req.url!, `http://${req.headers.host}`);

    if (pathname === '/stream') {
      async function* counter() {
        for (let i = 0; i < 5; i++) {
          await new Promise(r => setTimeout(r, 1000));
          yield `chunk ${i}\n`;
        }
      }
      return { body: counter(), headers: { 'content-type': 'text/plain' } };
    }

    return { body: 'Hello World' };
  });
}

运行与测试

  • 运行:npx tsx server.ts(或使用 ts-node)。

  • 访问:http://localhost:3000/stream,浏览器或 curl 将逐秒接收 "chunk" 数据。

  • 输出示例:

    bash 复制代码
    http://localhost:3000
    [访问 /stream,每秒输出一行:]
    chunk 0
    chunk 1
    chunk 2
    chunk 3
    chunk 4

总结

迭代器模式通过分离遍历逻辑,提供灵活的集合访问方式。JavaScript 的迭代器协议为其提供了底层支持,而异步迭代器进一步扩展到异步场景,如 Node.js 的自定义 serve 示例。通过 async *Readable.from,请求流和响应流清晰转换,实现了类似 Deno.serve 的简洁 API。这展示了迭代器模式在现代 Web 开发中的强大应用。

相关推荐
爱海贼的无处不在2 小时前
一个需求竟然要开14个会:程序员的日常到底有多“会”?
后端·程序员
IT_陈寒3 小时前
Java 性能优化:5个被低估的JVM参数让你的应用吞吐量提升50%
前端·人工智能·后端
南囝coding3 小时前
《独立开发者精选工具》第 018 期
前端·后端
绝无仅有3 小时前
数据库MySQL 面试之死锁与排查经验总结
后端·面试·github
小桥风满袖4 小时前
极简三分钟ES6 - ES9中for await of
前端·javascript
用户384958730694 小时前
Spring Boot 集成 Redis 的完整流程
后端
编程贝多芬4 小时前
Promise 的场景和最佳实践
前端·javascript
Asort4 小时前
JavaScript 从零开始(四):基础语法详解——从变量声明到数据类型的完全指南
前端·javascript
木木jio4 小时前
前端大文件分片上传 —— 基于 React 的工程化实现
前端·javascript