
Iterator(イテレータ)は、プログラミングでも超よく出てくる重要パターンの一つです。
このパターンは、コレクション(配列・リスト・ツリーなど)の要素に順番にアクセスする仕組みを統一的に提供するデザインパターンです。
内部構造を意識せずに、一つずつ順に取り出すことができるのが特徴です。
データ構造(リスト・ツリーなど)の中身を知らなくても、一貫した方法で要素を順番に走査できるようにするのが目的ですね。
構造イメージ
| 役割 |
説明 |
| Iterator(反復子) |
要素を順番に取り出すインターフェース(next()など) |
| ConcreteIterator |
実際のデータ構造に合わせた実装 |
| Aggregate(集合体) |
要素を持つオブジェクト |
| ConcreteAggregate |
Iteratorを生成する |
サンプルコード
自作コレクションを順番に取り出すIteratorサンプルです。
Aggregate(集合体)
class NameRepository {
constructor() {
this.names = ["Alice", "Bob", "Charlie"];
}
// Iteratorを返すメソッド
getIterator() {
return new NameIterator(this.names);
}
}
Iterator(イテレータ)
class NameIterator {
constructor(names) {
this.index = 0;
this.names = names;
}
hasNext() {
return this.index < this.names.length;
}
next() {
if (this.hasNext()) {
return this.names[this.index++];
}
return null;
}
}
Client(利用側)
const repo = new NameRepository();
const iter = repo.getIterator();
while (iter.hasNext()) {
console.log(iter.next());
}
// 出力:
// Alice
// Bob
// Charlie
JavaScriptの「標準イテレータ」との関係
JavaScriptではこのパターンが言語仕様として組み込まれています。
つまり、以下のように自作クラスでもSymbol.iteratorを定義すればOKなんです。
class MyCollection {
constructor(items) {
this.items = items;
}
// イテレータを定義
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
} else {
return { done: true };
}
}
};
}
}
// 利用側
const data = new MyCollection(["Alice, "Bob", "Charlie"]);
for (const name of data) {
console.log(name);
}
// 出力:
// Alice
// Bob
// Charlie
メリット
・内部構造を隠蔽しながら要素にアクセスできる
・データ構造が変わっても、同じ使い方で走査できる
・複数のコレクション形式を同一の方法で扱える(多態性)
デメリット
・イテレータを個別実装するとクラスが増える
・シンプルな構造(配列など)ではオーバーエンジニアリングになりやすい
あとがき
コレクションの中身を知らずに順番にアクセスする、リスト、ツリー、マップ、カスタムコレクションなどの場合、内部構造を隠せて、統一的に走査できます。
Javascriptでは、Iterator機能がネイティブで実装されていますので使いこなせるようになると、プログラミングスキルがアップするでしょうね。
個人的には、Javascriptでは、Sort()関数をよく使いますけど・・・
0 件のコメント:
コメントを投稿