この記事で紹介するデザインパターン
イテレータ(Iterator) パターン
どんなデザインパターン?
いつもどおりウェキペディアから参照
Iterator パターン(イテレータ・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とする。
ポイントは以下だろうか
・集合オブジェクト(要素の集まり)に対して順番にアクセスする方法を提供する
・集合オブジェクト(要素の集まり)自体には実装せず、外部イテレータクラスとして実装することで、集合オブジェクトがどのように集合データを保持しているか使う側は気にしなくて良い
集合オブジェクトの例
集合オブジェクトの例は
・本と本棚
・ビールケースとビール
のように要素とその要素の入れ物みたいな関係です。
Rubyのコードで簡単な例を紹介してみる
登場人物
・MyFileクラス(要素)
・Directoryクラス(MyFileの集合を保持)
・DirectoryIteratorクラス。要素の順番にアクセスする手段を提供するクラス
# MyFileクラス
1 2 3 4 5 6 7 8 9 |
class MyFile def initialize(name) @name = name end def name @name end end |
ファイル名だけを属性に持つシンプルなクラス。
次にファイルを格納できるディレクトリクラス。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Directory def initialize() @files = [] end def files @files end def add_file(file) files << file end def [](index) @files[index] end def length @files.length end end |
最後に要素(ファイル)に順番にアクセスする機能を提供するイテレータクラス。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class DirectoryIterator def initialize(directory) @directory = directory @index = 0 end def has_next? @directory.length > @index end def next_file file = self.has_next? ? @directory[@index] : nil @index += 1 file end end |
directoryとindexを属性に持つ。
next_fileでファイルのアクセスすることができる。
コードをコンソールから試してみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
3つのファイルを作成 [77] pry(main)> my_file1 = MyFile.new("myfile1") [78] pry(main)> my_file2 = MyFile.new("myfile2") [79] pry(main)> my_file3 = MyFile.new("myfile3") ディレクトリを作成し先程作成した3ファイルを格納 [81] pry(main)> directory = Directory.new("directory") [82] pry(main)> directory.add_file(my_file1) [83] pry(main)> directory.add_file(my_file2) [84] pry(main)> directory.add_file(my_file3) 確認 [89] pry(main)> directory.files => [#<MyFile:0x00007f9625e6e5e8 @name="myfile1">, #<MyFile:0x00007f962c328858 @name="myfile2">, #<MyFile:0x00007f9625bd7618 @name="myfile3">] イテレータを作成 [94] pry(main)> iterator = DirectoryIterator.new(directory) ファイルのアクセスしてみる [101] pry(main)> iterator.next_file => #<MyFile:0x00007f9625e6e5e8 @name="myfile1"> [102] pry(main)> iterator.next_file => #<MyFile:0x00007f962c328858 @name="myfile2"> [103] pry(main)> iterator.next_file => #<MyFile:0x00007f9625bd7618 @name="myfile3"> [105] pry(main)> iterator.next_file => nil whileを使って要素全体にアクセスすることもできる [114] pry(main)> while iterator.has_next? [114] pry(main)* file_name = iterator.next_file.name [114] pry(main)* p file_name [114] pry(main)* end "myfile1" "myfile2" "myfile3" |
ちなみに
今回紹介したパターンは外部イテレータと呼ばれる。
外部イテレータの他に、内部イテレータというものもあり、Rubyではこちらのほうが一般的かもしれない。
内部イテレータとは、集合オブジェクト自身が順次アクセスする手段を提供するパターンのことを言う。
例えば、以下のようなRubyのarrrayは
1 2 3 4 5 6 7 8 |
[118] pry(main)> array = [1,2,3] => [1, 2, 3] [119] pry(main)> array.each do |a| [119] pry(main)* p a [119] pry(main)* end 1 2 3 |
このように、array(重合オブジェクト)自身にeachというメソッドがあり、arrayの中身にアクセスすることができる。
まとめ
・イテレータパターンとは集合オブジェクトに対して順次アクセスする機能を提供することを可能にする
・集合自身とは別にその機能を提供するクラスがある