【Iteratorパターン】C言語で見かけるぐらい頻出のデザインパターン【コード例:Java、Ruby】

前書き:デザインパターンという認識がなかった

Iteratorデザインパターンは、配列/List/Mapなどのコレクション(要素の集合)を順次アクセスする際に、どのようなコレクションであっても同じAPIで操作可能にする設計方法です。

あまりにも一般的な手法であり、私はIteratorパターンがGoF(Gang of Four)デザインパターンという認識がありませんでした。オブジェクト指向を言語サポートしていないC言語ですら、Iteratorパターンの実装があるぐらいです。様々な場面で見かける機会が多いデザインパターンではないでしょうか。

本記事では、復習の意味を込めて、Iteratorパターンの実装例をJavaとRubyで紹介します。

              

Iteratorパターンの概要 

本記事では、複数のPackageクラスを管理(集約)するRepositoryクラスに対して、Iteratorデザインパターンを適用します。下図は登場人物となるクラス、下表は各クラスの役割説明です。

クラス名 役割
Aggregate 集合(コレクション)を表すインターフェース
Iterator 集合(コレクション)を操作するためのインターフェース
Package パッケージを表すクラス
Repository パッケージの格納先であるリポジトリを表すクラス
RepositoryIterator リポジトリを操作するためのクラス

                           

最終的な実装のイメージとしては、リポジトリの中にあるパッケージを一つずつ、iteratorインターフェースで取り出していきます。その過程で重要になるのは、Iteratorインターフェースの持つhasNext()とnext()です。次の要素(= パッケージ)が存在するかをhasNext()で調べ、次の要素が存在する場合はnext()で要素を取得します。

                            

Iteratorパターンの実装例:Java

Javaの場合は、Iteratorインターフェースがjava.util.Iteratorとして提供されているため、Iteratorインターフェースを改めて定義する必要がありません。

実装例は、以下の通りです。Aggregate、RepositoryIterator、Repository、Packageの実装を順に示した後、それらを操作するAppクラスの実装を示します。

               

以下、実行例です。リポジトリにパッケージを3個(パッケージ名称はbasename、gtk+3.0、build-essential)追加した後に、それらを取り出し、パッケージ名称を表示しています。

ちなみに、java.util.Collectionインターフェースにもiterator()が存在します。つまり、Collectionインターフェースを実装しているList/Set(Java標準API)では、上記のiteratorパターンを実装せずに使用できます。

また、Javaにはjava.lang.Iterableインターフェースも存在します。拡張For文を用いて集合を操作する際は、Iterableインターフェースを実装します。

                            

Iteratorパターンの実装例:Ruby

Rubyは、そもそもインターフェースの仕組みが言語仕様として存在しません。インターフェースと同じ役割を果たすクラスを用意すると、段々と形式張った言語(静的型付け言語)に見えてきます。

Rubyの生みの親である”まつもとゆきひろ氏”も、インターフェース(正確にはmodule)を使いすぎるとRubyっぽくなくなると発言しています。

 

Rubyには、Iterator自身が次の要素への処理を決定する「内部Iterator」と呼ばれる仕組みが存在します。Javaのサンプルコードで示したように、hasNext()やnext()を呼び出す方法は、外部Iteratorと呼ばれています。

外部Iteratorと比較して内部Iteratorは実装の柔軟性に欠けるものの、どのようなコレクションであっても(配列、連想配列などのいずれであっても)、各コレクションを同じように操作する方法を提供してくれます。

内部Iteratorの具体例として、Rubyはeachメソッドでコレクションを操作できます。以下、実装例です。

上記のコードには、「抽象メソッド(Iterator、Aggregation)」および「Iteratorインターフェースの実装(RepositoryIterator)」が存在しません。

    

以下、実行例です。

Ruby側は全体的にシンプルな実装であり、Javaの厳格さと比べると、遥かにRubyが書きやすい言語ですね(実装規模が大きくなると、Javaが良いな、となりますが)

           

他のデザインパターンに関して

GoFデザインパターン(23種類)に関しては、以下の記事でまとめてあります。

【オブジェクト指向】全23種類のGoFデザインパターンに関する説明と参考書籍

                   

おすすめ