概要
コンテナオブジェクトと、その要素へのアクセス、走査のためのインターフェイスを独立に実装したパターン
以下の4種類のクラスからなる
- Iteratorクラス(コンテナオブジェクトの要素にアクセス、走査するためのインターフェイスを定義)
- ConcreteIteratorクラス(1.で定義したインターフェイスを実装する)
- Aggregateクラス(1.を生成するためのインターフェイスを定義)
- ConcreteAggregateクラス(3.で定義したインターフェイスに対して適切な2.のインターフェイスを生成して返す)
具体例と実装
エンタメサイトの一覧ソート機能を例にすると
上記1~4はそれぞれ
- 作品イテレータークラス
- 新着順クラス
- 作品一覧クラス
- 映画一覧クラス
が対応する。
コードの詳細は以下
iterator.rb
class Iterator
  def initialize(aggregate)
    @aggregate = aggregate
    @index = 0
  end
  def has_next?
  end
  def next_item
  end
end
recent_iterator.rb
class RecentIterator < Iterator
  def initialize(aggregate)
    @aggregate = aggregate.sort do |(_k1, v1), (_k2, v2)|
      v1[:published_at] <=> v2[:published_at]
    end
  @index = 0
  end
  def has_next?
    @index < @aggregate.size
  end
  def next_item
    @index = @index + 1
    item = self.has_next? ? @aggregate.get_by_index(@index) : nil
  end
end
aggregate.rb
class Aggregate
  def initialize
    @works = []
  end
  def get_by_index(index)
  end
  def size
  end
end
MovieAggregate.rb
class MovieAggregate < Aggregate
  def initialize
    @works = []
  end
  def add_work(work)
    @works << work
  end
  def get_by_index(index)
    @works[index]
  end
  def size
    @works.size
  end
end
client.rb
movie_list = MovieAggregate.new
# 映画作品を追加していく
movie_list.add_work(...)
...
iterator = RecentIterator(movie_list)
while iterator.has_next?
  work = iterator.next_item
  puts work.title
end 
メリット
- コンテナオブジェクトと走査のインターフェイスが独立しているため、コンテナの内部表現を公開せずに要素へのアクセスを提供出来る
- あるコンテナオブジェクトに対して複数の走査をサポートできる
- 異なるコンテナに対して、同一のインターファイスで要素にアクセス出来る
まとめ
コンテナの内部表現を隠蔽して要素へのアクセスを提供するパターン
あるコンテナの要素への複数のアクセス方法が必要な場合、または複数のコンテナの要素に対してアクセスする共通のインターファイスが必要な場合に用いる