Iterator パターン
id:hyuki さんのデザインパターン本の Iterator パターンを Ruby に移植してみます。
#!/usr/local/bin/ruby class Iterator def has_next end def next end end class Aggregate def iterator end end class Book attr_reader :name def initialize(name) @name = name end end class BookShelf < Aggregate def initialize @books = Array.new @last = 0 end def [](index) @books[index] end def append_book(book) @books[@last] = book @last += 1 end def length @last end def iterator BookShelfIterator.new(self) end end class BookShelfIterator < Iterator def initialize(bookshelf) @bookshelf = bookshelf @index = 0 end def has_next if (@index < @bookshelf.length) return true else return false end end def next book = @bookshelf[@index] @index += 1 return book end end shelf = BookShelf.new shelf.append_book(Book.new("Around the World in 80 Days")) shelf.append_book(Book.new("Bible")) shelf.append_book(Book.new("Cinderella")) shelf.append_book(Book.new("Daddy-Long-Legs")) it = shelf.iterator while (it.has_next) book = it.next puts book.name end
これを実行すると
$ ruby iterator.rb Around the World in 80 Days Bible Cinderella Daddy-Long-Legs
となります。
Java のコードをほぼそのままで getBookAt(int index)
を [](index)
にしてるところが違うぐらいのコードです。でもこれですと、
- Iterator クラス、Aggregate クラスはコードを実行する上では全く意味を為していない
- Java の場合は interface として書けるので意味がある
- while 文でイテレータを回すあたりが ruby らしくない
という感じがします。そこで http://www.freeml.com/message/patterns/997 で高橋さんが書いたコードのように each を使うようにしてみます。
#!/usr/local/bin/ruby class Book attr_reader :name def initialize(name) @name = name end end class BookShelf def initialize @books = Array.new end def append_book(book) @books.push(book) end def each @books.each do |book| yield(book) end end end shelf = BookShelf.new shelf.append_book(Book.new("Around the World in 80 Days")) shelf.append_book(Book.new("Bible")) shelf.append_book(Book.new("Cinderella")) shelf.append_book(Book.new("Daddy-Long-Legs")) shelf.each do |book| puts book.name end
shelf.each do |book| ... end
で回せるようになって ruby らしいコードになりました。BookShelfIterator として機能していた箇所は yield を使った each の中に内包されたので、クラスは二つだけになりました。
高橋さんのコードの最終形にもあるとおり、この場合の BookShelf は Array を継承するだけでも良かったりします。ただし、その場合 BookShelf がデータ構造を抽象化していることにはならないのかもしれません。
yield がすぐに頭に出てくるようになるにはもっと修行が必要そうです。
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (397件) を見る