Help us understand the problem. What is going on with this article?

C#で学ぶデザインパターン入門 ①Iterator

More than 3 years have passed since last update.

概要

@hyuki 先生著の『Javaで学ぶデザインパターン入門』(2004年、SB Creative)の1章ずつをベースに、サンプルコードをC#で置き換えながら勉強していく記事です。

※著者の @hyuki 先生には適切に書籍への参照を入れれば問題ない旨ご確認いただいています。

デザインパターンをなんで勉強したいのか?

  • あちらこちらで使われている(気がする)ので、一度勉強したい
    • 正しいコンテキストでコードを読み、修正できるようにする一助に
  • インターフェース未だに腑に落ちない
    • C#を触り始めて1年くらい経ってるのに...
  • 「とりあえず動くコードやけど、似たような結果を期待したコードってたぶんいっぱいあるし、ベストプラクティス的なのありそう」と感じる局面が多い
    • こういうときはこう書こうという引き出しを増やす。
  • デザインパターンなんとなく勉強してみたい勢もいっぱいいるはずなので、何かしら提示してみたい

やっていく中で気を付けたいこと

一部デザインパターンはアンチパターンに転落していますが、他のデザインパターンは日常的に使用され、コードの適応性を向上させています。

と書いてあるので、使う上で気を付けるべきことがないか調べつつ書いていきたい(続けられる範囲で)。

  • 読むだけだと頭に残らないかもしれないので、サンプルコードをC#で書き換えてみて、動作を確認する。
  • 考えたこととか、疑問点とかは残しておいて、公開した後でも追記・修正していく。

本題

Iteratorパターン
第1回はIteratorパターンです。Iteratorパターンはコレクションの要素にループ変数をiとしてfor文で順にアクセスしていく際の変数iの働きを抽象化し、一般化したものです。

コレクションの要素を列挙する手段を提供して、具体的な列挙方法をコレクションから隠ぺいすることができるんですね!

サンプルコード

早速具体的な事例を見てみましょう。『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に掲載されているコードをC#で(大体)書き換えます。

// コンソールアプリケーションで実行を確認しました
static void Main(string[] args)
        {
            BookShelf bookShelf = new BookShelf();
            bookShelf.appendBook(new Book("Around the world in 80 days"));
            bookShelf.appendBook(new Book("Bible"));
            bookShelf.appendBook(new Book("Cinderella"));
            bookShelf.appendBook(new Book("Daddy-Long-Legs"));
            IIterator it = bookShelf.Iterator();
            while(it.HasNext())
            {
                Book book = (Book)it.Next();
                Console.WriteLine(book.Name);
            }
            // 実行結果
            // Around the world in 80 days
            // Bible
            // Cinderella
            // Daddy-Long-Legs

       // 実行が一瞬で終わって確認できないので、キーの入力を待ちます
            Console.ReadLine();
        }

        // 集合体を表すインターフェース
        public interface IAggregate
        {
            IIterator Iterator();
        }

        // 数え上げ、スキャンを表すインターフェース
        public interface IIterator
        {
            bool HasNext();
            object Next();
        }

        // 本を表すクラス
        public class Book
        {
            public string Name { get; set; }
            public Book(string name)
            {
                this.Name = name;
            }
        }

        // 本棚を表すクラス
        public class BookShelf : IAggregate
        {
            List<Book> books = new List<Book>(); 
            int Last { get; set; } = 0;
            public Book GetBookAt(int index)
            {
                return books[index];
            }
            public void appendBook(Book book)
            {
                this.books.Add(book);
                Last++;
            }
            public int GetLength()
            {
                return this.Last;
            }
            public IIterator Iterator()
            {
                return new BookShelfIterator(this);
            }
        }

        // 本棚をスキャンするクラス
        public class BookShelfIterator : IIterator
        {
            BookShelf BookShelf { get; set; }
            int Index { get; set; }
            public BookShelfIterator(BookShelf bookShelf)
            {
                this.BookShelf = bookShelf;
                this.Index = 0;
            }
            public bool HasNext()
            {
                if(Index < BookShelf.GetLength())
                {
                    return true;
                } else {
                    return false;
                }
            }
            public object Next()
            {
                Book book = BookShelf.GetBookAt(Index);
                this.Index++;
                return book;
            }
        }

効能

  • Iteratorパターンを使ったループはIAggregateの実装に依存しない。例えば、サンプルのBookShelfクラスがList<T>でなく、ArrayListを使用する実装に変更されても、while部分は書き直さなくて済む。

使用上の注意

『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に書いてある内容にとても納得感がありました。

  • 「次」は間違いやすい
    IIterator内のNext()は現在の要素を返し、Indexを1進めるので、メソッド名を正確に書くとするとReturnCurrentElementAndAdvanceToNextPositionになります。業務のコードで使われてるの見たことないけど、実際メソッド名は工夫して使うのかなぁ...
  • 「最後」も間違いやすい
    IIterator内のHasNext()も、最後の要素を取得した後にHasNext()falseになることを注意して使わない実装・使用しないと「最後の要素だけ取り出し忘れてしまった...」ということになってしまいかねないですね。本に書いてある「次にnextメソッドを呼んでも大丈夫かどうかを調べるもの」という覚え方、よさげです。

関連しているパターン

記事にしたらリンクもつく予定...

感想や疑問

  • いまだにinterface、あれば使うものの、自分で1から書くときに使わなさそう(抽象クラスとの違いを理解してない?)
  • あるオブジェクトAから別のオブジェクトB(コレクション)のデータを利用するとき、Bの構造に依存せず利用したい...みたいな場面に出くわしたことない?のでありがたみをまだ実感できてないので、そういうときがきたら使ってみよう。

C#で学ぶデザインパターン入門

①Iterator
②Adapter
③Template Method
④Factory Method
⑤Singleton
⑥Prototype
⑦Builder
⑧AbstractFactory
⑨Bridge
⑩Strategy
⑪Composite Pattern
⑫Decorator Pattern

toshi0607
最近はGoとServerlessが好きです!
https://toshi0607.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした