LoginSignup
11
11

More than 5 years have passed since last update.

コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。

どういうパターン?

アイテムを持つコンテナオブジェクトと、アイテムの要素を列挙するイテレータを独立させるパターン

  • コンテナクラス毎にイテレータを独自実装する必要がない
  • イテレータに機能を追加したい場合、コンテナクラス毎に機能を追加する必要が無い

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

参考

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11