【デザインパターン】イテレータパターンとは?データ構造を柔軟に扱う設計手法を解説

イテレータパターンは、コレクション(配列やリストなど)の内部構造を隠蔽しつつ、要素を順番に走査する方法を提供するデザインパターンです。

C#を例に具体的な実装を紹介しますが、JavaやPythonなど他の言語でも応用できます。このパターンを使うことで、異なるデータ構造に対して統一的なアクセス手法を確立し、柔軟な設計が可能になります。

目次

イテレータパターンとは?

イテレータパターン(Iterator Pattern)は、オブジェクトの集合(コレクション)の要素に順番にアクセスするための設計手法です。
通常、配列やリストなどのデータ構造を直接操作すると、その内部実装に依存してしまいます。しかし、イテレータパターンを使えば、データ構造の詳細を隠しながら、統一的な方法でデータを扱えます。

イテレータパターンのメリット

イテレータパターンを採用することで、以下のようなメリットがあります。

  • データ構造を意識せずに要素を取得できる
  • 異なるコレクション型を統一的に扱える
  • 要素の取得方法を柔軟にカスタマイズ可能
  • 拡張性が向上し、コードの可読性が高まる

C#でのイテレータパターンの実装

C#では、IEnumerable<T> インターフェースを利用してイテレータパターンを簡単に実装できます。
以下では、独自のコレクションクラスを作成し、カスタムイテレータを実装する方法を紹介します。

1. コレクションを表すクラス

まず、IEnumerable<T> を実装したカスタムコレクションクラスを作成します。

using System;
using System.Collections;
using System.Collections.Generic;

// カスタムコレクション
class CustomCollection<T> : IEnumerable<T>
{
    private List<T> _items = new List<T>();

    // 要素を追加するメソッド
    public void Add(T item) => _items.Add(item);

    // イテレータを取得
    public IEnumerator<T> GetEnumerator() => new CustomIterator<T>(_items);

    // 非ジェネリック版のGetEnumerator(IEnumerableの実装)
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

IEnumerable<T> を実装すると、C#の foreach を使って要素を簡単にループできます。

2. イテレータを表すクラス

次に、IEnumerator<T> を実装して、カスタムイテレータを作成します。

// カスタムイテレータ
class CustomIterator<T> : IEnumerator<T>
{
    private List<T> _items;
    private int _position = -1;

    public CustomIterator(List<T> items) => _items = items;

    // 現在の要素を取得
    public T Current => _items[_position];

    object IEnumerator.Current => Current;

    // 次の要素へ移動
    public bool MoveNext()
    {
        if (_position + 1 < _items.Count)
        {
            _position++;
            return true;
        }
        return false;
    }

    // イテレータをリセット
    public void Reset() => _position = -1;

    // リソースの解放(今回は特に不要)
    public void Dispose() { }
}

MoveNext() で次の要素に進み、Current で現在の要素を取得します。


3. 実際の使用例

作成したカスタムコレクションを使って、foreach で要素を取得してみます。

class Program
{
    static void Main()
    {
        CustomCollection<string> collection = new CustomCollection<string>();

        collection.Add("Apple");
        collection.Add("Banana");
        collection.Add("Cherry");

        foreach (var item in collection)  // イテレータが内部的に動作
        {
            Console.WriteLine(item);
        }
    }
}
Apple
Banana
Cherry

4. C#のyield return を使った簡単な実装

C#では、IEnumerator<T> を直接実装しなくても、yield return を使うことで簡単にイテレータを作成できます。

using System;
using System.Collections.Generic;

// カスタムコレクション
class CustomCollection<T>
{
    private List<T> _items = new List<T>();

    public void Add(T item) => _items.Add(item);

    // `yield return` を使った簡単なイテレータ
    public IEnumerable<T> GetItems()
    {
        foreach (var item in _items)
        {
            yield return item;  // 要素を順番に返す
        }
    }
}

class Program
{
    static void Main()
    {
        CustomCollection<string> collection = new CustomCollection<string>();

        collection.Add("Dog");
        collection.Add("Cat");
        collection.Add("Rabbit");

        foreach (var item in collection.GetItems())
        {
            Console.WriteLine(item);
        }
    }
}
Dog
Cat
Rabbit

yield return を使うと、IEnumerator<T> の実装が不要になり、シンプルなコードでイテレータを実現できます。

  • IEnumerable<T> を実装すると、foreach でループできるようになる
  • IEnumerator<T> を使うと、カスタムイテレータを作成できる
  • yield return を使えば、簡単にイテレータを作れる
  • データ構造を意識せずに統一的な方法で要素を走査できる

イテレータパターンを使うことで、コードの再利用性が向上し、異なるデータ構造を一貫した方法で扱うことができます!

イテレータパターンを使うべきケース

イテレータパターンは、以下のような状況で特に有効です。

  • 異なるデータ構造(リスト、配列、ツリー、グラフなど)を統一的に走査したいとき
  • データ構造の内部実装を隠蔽し、安全にアクセスさせたいとき
  • 独自のルールで要素の取得順序やフィルタリングをしたいとき
  • foreach などの構文を自作のコレクションで利用したいとき
  • 順番にアクセスする処理を統一し、コードの可読性と拡張性を向上させたいとき

このパターンを使うことで、データ構造に依存しない柔軟な設計が可能になります。

まとめ

  • イテレータパターンは、データ構造を意識せずに要素を順番に走査するためのデザインパターン。
  • IEnumerable<T> を実装すると、foreach で統一的にデータを扱える。
  • IEnumerator<T> を使えば、カスタムイテレータを作成できる。
  • yield return を活用すると、簡潔なコードでイテレータを実装可能。
  • 異なるデータ構造(リスト、ツリー、グラフなど)でも、統一的なアクセスが可能になり、コードの可読性・拡張性が向上する。

イテレータパターンを活用すると、異なるデータ構造を統一的に扱え、コードの柔軟性や再利用性が向上します。特に、foreach を使えるようにしたり、データアクセスをカプセル化することで、保守性の高いプログラムを実現できます。

C#では IEnumerable<T>yield return を活用することで、簡単にイテレータを実装できるため、ぜひ実践してみてください!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次