今更デザパタ:Iteratorパターン
Rubyで書いていきます。RubyにはIteratorがすでにあるという話はおいといて。
テスト駆動で行きましょう。まずはテストケース test_iterator.rb
[]require[] []'[][]test/unit[][]'[] []require[] []'[][]iterator[][]'[] []class [][]TestIterator[] []<[] []Test[][]::[][]Unit[][]::[][]TestCase[] []def [][]setup[] []@bookshelf[] []=[] []Bookshelf[][].[][]new[] []@bookshelf[][].[][]append_book[][]([][]Book[][].[][]new[][]("[][]AWDwR[][]")[] [])[] []@bookshelf[][].[][]append_book[][]([][]Book[][].[][]new[][]("[][]RailsRecipes[][]")[] [])[] []@bookshelf[][].[][]append_book[][]([][]Book[][].[][]new[][]("[][]GettingReal[][]")[] [])[] []@bookshelf[][].[][]append_book[][]([][]Book[][].[][]new[][]("[][]AjaxInAction[][]")[] [])[] []@bookshelf[][].[][]append_book[][]([][]Book[][].[][]new[][]("[][]RideOnRails[][]")[] [])[] []end[] []def [][]test_book[] []assert_equal[][]("[][]test book[][]",[] []Book[][].[][]new[][]("[][]test book[][]").[][]name[][])[] []assert_not_equal[][]("[][]test book[][]",[] []Book[][].[][]new[][]("[][]test book?[][]").[][]name[][])[] []end[] []def [][]test_bookshelf[] []assert_equal[][]("[][]AWDwR[][]",[] []@bookshelf[][].[][]get_book_at[][]([][]0[][]).[][]name[][])[] []assert_equal[][]("[][]AjaxInAction[][]",[] []@bookshelf[][].[][]get_book_at[][]([][]3[][]).[][]name[][])[] []assert_kind_of[][]([][]Numeric[][],[] []@bookshelf[][].[][]length[][])[] []assert_equal[][]([][]nil[][],[] []@bookshelf[][].[][]get_book_at[][]([][]@bookshelf[][].[][]length[][]))[] []assert_kind_of[][]([][]Iterator[][],[] []@bookshelf[][].[][]iterator[][])[] []end[] []def [][]test_iterator[] []iterator[] []=[] []@bookshelf[][].[][]iterator[] []while[][]([][]iterator[][].[][]has_next?[][])[] []assert_nothing_raised[][]{[] []iterator[][].[][]next[][].[][]name[] []}[] []end[] []assert_not_equal[][]([][]true[][],[] []iterator[][].[][]has_next?[][])[] []assert_equal[][]([][]nil[][],[] []iterator[][].[][]next[][])[] []end[] []end[]
続いて、実装。iterator.rb
[]class [][]Aggregate[] []def [][]iterator[] []raise[] []NotImplementedError[][].[][]new[] []end[] []end[] []class [][]Iterator[] []def [][]has_next?[] []raise[] []NotImplementedError[][].[][]new[] []end[] []def [][]next[] []raise[] []NotImplementedError[][].[][]new[] []end[] []end[] []class [][]Bookshelf[] []<[] []Aggregate[] []def [][]initialize[] []@books[] []=[] []Array[][].[][]new[] []end[] []def [][]get_book_at[][]([][]index[][])[] []@books[][][[][]index[][]][] []end[] []def [][]append_book[][]([][]book[][])[] []unless[] []Book[] []===[] []book[] []raise[] []TypeError[] []end[] []@books[] []<<[] []book[] []end[] []def [][]iterator[] []BookshelfIterator[][].[][]new[][]([][]self[][])[] []end[] []def [][]length[] []@books[][].[][]length[] []end[] []end[] []class [][]Book[] []attr_accessor[] []:name[] []def [][]initialize[][]([][]name[][])[] []@name[] []=[] []name[] []end[] []end[] []class [][]BookshelfIterator[] []<[] []Iterator[] []def [][]initialize[][]([][]bookshelf[][])[] []@index[] []=[] []0[] []@bookshelf[] []=[] []bookshelf[] []end[] []def [][]has_next?[] []if[] []@index[] []<[] []@bookshelf[][].[][]length[] []true[] []else[] []false[] []end[] []end[] []def [][]next[] []ret[] []=[] []@bookshelf[][].[][]get_book_at[][]([][]@index[][])[] []@index[] []+=[] []1[] []return[] []ret[] []end[] []end[]
IteratorとAggregateはインターフェースとして使いたいので、それぞれのメソッドにはraise NotImplementedError.newしておく。
こういうのを作ると、Rubyのブロックのありがたさがわかるなぁ。