コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。
どういうパターン?
アイテムを持つコンテナオブジェクトと、アイテムの要素を列挙するイテレータを独立させるパターン
- コンテナクラス毎にイテレータを独自実装する必要がない
- イテレータに機能を追加したい場合、コンテナクラス毎に機能を追加する必要が無い
rubyでは、内部イテレータが一般的
Rubyでは、Arrayなどのコンテナオブジェクトが、eachなどのイテレートするメソッドを持っている内部イテレータである
内部イテレータ : ruby の Array のように、イテレータがコンテナ内部に実装されている
外部イテレータ : Iterator パターン のように、コンテナと、イテレータが独立したオブジェクトである
内部イテレータだと、コンテナ毎に必要なイテレータを実装する必要があるから不便じゃない?
なので、ruby の場合、each だけ実装すれば、Enumerable を mix-in することで、Enumerable が持つ機能を全て実現できるようになっている
items.rb
class Items
include Enumerable
def initialize
@items = []
end
def <<(item)
@items << item
end
def each
@items.each do |item|
yield item
end
end
end
items = Items.new
items << 'abc'
items << 'def'
items << 'ghi'
# Items には実装されていない #each_with_index が使えている!!
items.each_with_index do |item, i|
puts "#{i} : #{item}"
end
上のような例の場合、そもそも配列を使えば良いんじゃない?
その通り
基本的には、配列で済むならば配列を使うべき
例えばこんな風に、要素毎に何かしら処理を施した結果を、each したい。みたいな時に便利
downloader.rb
require "open-uri"
class Titles
include Enumerable
def initialize
@urls = []
end
def <<(url)
@urls << url
end
def each
@urls.each do |url|
open(url).read =~ %r|<title>(.*)</title>|
yield $1
end
end
end
t = Titles.new
t << "http://www.google.co.jp"
t << "http://www.yahoo.co.jp"
puts t.map(&:upcase)
# GOOGLE
# YAHOO! JAPAN