Rubyによるデザインパターンまとめ : iterator
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (66件) を見る
利用する場面
あるオブジェクトが複数の子要素をもっており、それを連続して走査したい場合。
(この親要素を集約オブジェクトと呼ぶ。)
方法
- 外部イテレータ
- 集約オブジェクトとは別にiterateするためのオブジェクトをつくる。
- 外部イテレータに#nextみたいなメッセージを送ることで、外部からiterateを操作する。
- 内部イテレータ
- 集約オブジェクトと別にiterateするためのオブジェクトをつくらない。
- ブロックを受け取って、後は内部でガーッとiterateする。その過程でブロックをyieldする。
その他
- Javaではもっぱら外部イテレータを使うらしい。
- Rubyには組み込みメソッドで色んな便利なiteratorが存在する。
- ほとんどは内部イテレータだが、Fileオブジェクトは外部イテレータとして使うことができる。
File#readline
で1つすすみ、File#eof?
で位置を確認する。
サンプル
外部イテレータ
class Parent attr_reader :children def initialize(*children) @children = children end end class Child def initialize(name) @name = name end end class ChildrenIterator def initialize(parent) @parent = parent @index = 0 end def has_next? @index < @parent.children.length - 1 end def child @parent.children[@index] end def next raise "no younger child" unless has_next? @index += 1 end end namihei = Parent.new(Child.new(:sazae), Child.new(:katsuo), Child.new(:wakame)) i = ChildrenIterator.new(namihei) while true do p i.child i.has_next? ? i.next : break end # => #<Child:0x007ff53c195aa8 @name=:sazae>, #<Child:0x007fa40307d290 @name=:katsuo>, #<Child:0x007fa40307d268 @name=:wakame>
内部イテレータ①:地に直接iterateするためのメソッドを書く。
class Parent attr_reader :children def initialize(*children) @children = children end end class Child def initialize(name) @name = name end end def iterate_children(parent) i = 0 while i < parent.children.size yield(parent.children[i]) i += 1 end end namihei = Parent.new(Child.new(:sazae), Child.new(:katsuo), Child.new(:wakame)) iterate_children(namihei) { |child| p child } # => #<Child:0x007f9a971976f0 @name=:sazae>, #<Child:0x007f9a97197650 @name=:katsuo>, #<Child:0x007f9a97197628 @name=:wakame>
内部イテレータ②:集約オブジェクトにiterateするためのメソッドを書く。
class Parent attr_reader :children def initialize(*children) @children = children end def iterate_children i = 0 while i < children.size yield(children[i]) i += 1 end end end class Child def initialize(name) @name = name end end namihei = Parent.new(Child.new(:sazae), Child.new(:katsuo), Child.new(:wakame)) namihei.iterate_children { |child| p child } # => #<Child:0x007f9a971976f0 @name=:sazae>, #<Child:0x007f9a97197650 @name=:katsuo>, #<Child:0x007f9a97197628 @name=:wakame>