デザインパターン第15回「Iteratorパターン」その2
以前書いた、デザインパターン第10回「Iteratorパターン」その1はこちらです。
以前書いたときは、ただ単にIEnumerableインターフェイスおよび、IEnumeratorインターフェイスを
そのまま利用しただけの例について、イテレータパターンを学習しました。
今回は、GoF本の中でも触れられているPreviousやSkipToなどの機能を実際にC#2.0で実装してみましょうというお話。
んでもって、せっかくなのでジェネリクス対応してしまいましょう。
実装内容について詳しくは解説しませんが、内容はコードおよびコメントを見れば一目瞭然かと。
Iteratorの定義
まずは、要素へのアクセスや走査を提供するインターフェイス(Iterator)を定義します。今回は、IEnumerator
/// <summary> /// IEnumeratorEx(T) /// ジェネリック コレクションに対する単純な反復処理をサポートします。 /// </summary> /// <typeparam name="T">列挙するオブジェクトの型</typeparam> public interface IEnumeratorEx<T> : IEnumerator<T> { /// <summary> /// カレントをひとつ前の要素へ戻します。 /// </summary> /// <returns> /// true:ひとつ前へ戻れた /// false:ひとつ前へ戻れなかった /// </returns> bool MovePrev(); /// <summary> /// カレントから指定要素数分スキップします。 /// </summary> /// <param name="num">スキップする要素数</param> /// <returns> /// true:スキップできた /// false:スキップできなかった /// </returns> bool SkipTo(int num); /// <summary> /// カレントを最初の要素へ移動します。 /// </summary> void MoveFast(); /// <summary> /// カレントを最終要素へ移動します。 /// </summary> void MoveLast(); /// <summary> /// カレントをオーバーフロー位置へ移動します。(最終要素+1) /// </summary> void SetOverflow(); }
ConcreteIteratorの作成
次に、実際にその拡張インターフェイス IEnumeratorExジェネリックイテレータ列挙子(ConcreteIterator)を作成します。
/// <summary> /// ジェネリックイテレータ列挙子 /// </summary> /// <typeparam name="T">要素の型</typeparam> public class IteratorEnumerator<T> : IEnumeratorEx<T> { #region メンバ /// <summary> /// ジェネリックイテレータ /// </summary> private Iterator<T> _iter; /// <summary> /// イテレータのカレント要素インデックス /// </summary> private int index = -1; #endregion #region コンストラクタ /// <summary> /// コンストラクタ /// </summary> /// <param name="iter">ジェネリックイテレータ</param> public IteratorEnumerator(Iterator<T> iter) { this._iter = iter; } #endregion #region IEnumerator<T> メンバ /// <summary> /// カレントの要素を取得します。 /// </summary> public T Current { get { return (T)_iter[index]; } } #endregion #region IEnumerator メンバ /// <summary> /// カレントの要素を取得します。 /// </summary> object IEnumerator.Current { get { //Resetされた状態の場合、最初の要素を返します。 if (index < 0) return _iter[0]; //SetOverflowされた状態の場合、最終要素を返します。 if (index > this._iter.Length - 1) return _iter[this._iter.Length - 1]; //カレント要素を返します。 return _iter[index]; } } /// <summary> /// 次の要素へ進める /// </summary> /// <returns> /// true:次の要素へ進めれた /// false:次の要素へ進めれなかった /// </returns> public bool MoveNext() { if (index < _iter.Length - 1) { index++; return true; } return false; } /// <summary> /// リセットします(最初の要素-1をカレントに) /// </summary> public void Reset() { index = -1; } #endregion #region IEnumeratorEx<T> メンバ /// <summary> /// 前の要素へ戻る /// </summary> /// <returns> /// true:前の要素へ戻れた /// false:前の要素へ戻れなかった /// </returns> public bool MovePrev() { if (index > 0) { index--; return true; } return false; } /// <summary> /// カレントから指定要素数分スキップします。 /// </summary> /// <param name="num"> /// スキップする要素数 /// +:Next /// -:Prev /// </param> /// <returns> /// true:スキップできた /// false:スキップできなかった /// </returns> public bool SkipTo(int num) { if (num < 0) { if (index + num >= 0) { index = num + index--; return true; } } else { if (num + index <= _iter.Length - 1) { index = num + index++; return true; } } return false; } /// <summary> /// カレントを最初の要素へ移動します。 /// </summary> public void MoveFast() { index = 0; } /// <summary> /// カレントを最終要素へ移動します。 /// </summary> public void MoveLast() { index = _iter.Length - 1; } /// <summary> /// カレントをオーバーフロー位置へ移動します。(最終要素+1) /// </summary> public void SetOverflow() { index = _iter.Length; } #endregion #region IDisposable メンバ public void Dispose() { IEnumeratorEx<T> em = _iter.GetEnumeratorEx(); while (em.MoveNext()) { //お掃除すべきときは、ちゃんとお掃除しましょうね^^ IDisposable dispo = em.Current as IDisposable; if (dispo != null) { dispo.Dispose(); } }; } #endregion }
Aggregateの定義
続きまして、イテレータオブジェクト(列挙子)を生成するためのインターフェイス(Aggregate)を定義します。今回は、IEnumerable
/// <summary> /// IEnumerableEx(T) /// 指定した型のコレクションに対する単純な反復処理をサポートする列挙子を公開します。 /// </summary> /// <typeparam name="T">列挙するオブジェクトの型</typeparam> public interface IEnumerableEx<T> : IEnumerable<T> { /// <summary> /// コレクションを反復処理できる列挙子(IEnumeratorEx)を返します。 /// </summary> /// <returns>列挙子T</returns> IEnumeratorEx<T> GetEnumeratorEx(); }
ConcreteAggregateの作成
最後に、実際にその拡張インターフェイス IEnumerableExイテレータオブジェクト(列挙子)を生成するクラス(ConcreteAggregate)を作成します。
/// <summary> /// ジェネリックイテレータ /// </summary> /// <typeparam name="T">要素の型</typeparam> public class Iterator<T> : IEnumerableEx<T> { #region メンバ /// <summary>イテレータ要素の配列</summary> private T[] _items; /// <summary>最大要素数</summary> private int _maxSize = 0; /// <summary>最終要素</summary> private int last = 0; #endregion #region 定数 /// <summary> /// 既定の最大要素数 (用途に応じて変更してね^ω^) /// </summary> private const int DEFAULT_MAX_SIZE = 10000; #endregion #region コンストラクタ /// <summary> /// デフォルトコンストラクタ /// </summary> public Iterator() { _maxSize = DEFAULT_MAX_SIZE; if (_maxSize <= 0) { throw new ArgumentException("最大要素数は0以上である必要があります。"); } this._items = new T[_maxSize]; } /// <summary> /// コンストラクタ /// </summary> /// <param name="maxSize">最大要素数</param> public Iterator(int maxSize) { if (maxSize <= 0) { throw new ArgumentException("最大要素数は0以上である必要があります。");} _maxSize = maxSize; this._items = new T[maxSize]; } #endregion #region プロパティ /// <summary> /// 要素数を取得します。 /// </summary> public int Length { get { return last; } } /// <summary> /// 要素を取得します。(インデクサ) /// </summary> /// <param name="index">要素のインデックス</param> /// <returns>要素T</returns> public T this[int index] { get { return _items[index]; } } #endregion #region メソッド /// <summary> /// 単一の要素をイテレータに追加します。 /// </summary> /// <param name="item">追加する要素T</param> public void AppendItem(T item) { // 最大要素数を超えて追加しようとしたら、それ以上はシカトします。 if (last +1 > _maxSize) {return;} _items[last] = item; last++; } /// <summary> /// 複数の要素をイテレータに追加します。 /// </summary> /// <param name="items">Tの配列</param> public void AppendItems(T[] items) { for (int i = 0; i < items.Length ; i++){ // 最大要素数を超えて追加しようとしたら、それ以上はシカトします。 if (last + 1 > _maxSize) { return; } _items[last] = items[i]; last++; } } /// <summary> /// 複数の要素をイテレータに追加します。 /// </summary> /// <param name="items">IEnumerableインターフェイス</param> public void AppendItems(IEnumerable<T> items) { foreach (T item in items){ // 最大要素数を超えて追加しようとしたら、それ以上はシカトします。 if (last + 1 > _maxSize) { return; } _items[last] = item; last++; } } #endregion #region IEnumerable<T> メンバ /// <summary> /// コレクションを反復処理できる列挙子を返します。 /// </summary> /// <returns>列挙子T</returns> IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new IteratorEnumerator<T>(this); } #endregion #region IEnumerable メンバ /// <summary> /// コレクションを反復処理できる列挙子を返します。 /// </summary> /// <returns>列挙子</returns> public IEnumerator GetEnumerator() { return new IteratorEnumerator<T>(this); } #endregion #region IEnumerableEx メンバ /// <summary> /// コレクションを反復処理できる列挙子(IEnumeratorEx)を返します。 /// </summary> /// <returns>列挙子T</returns> public IEnumeratorEx<T> GetEnumeratorEx() { return new IteratorEnumerator<T>(this); } #endregion }
以上です。お疲れさまでした。