ようこそ。睡眠不足なプログラマのチラ裏です。

デザインパターン第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

    }


以上です。お疲れさまでした。