Iterator (イテレータ) パターン
簡単に言うと、『集合体』の持っている『要素』を順番に数え上げるデザインパターンです。
『集合体』と『要素』とは。概念的なお話をしましょう。
例えば、ビールケースの中に一杯ビールが入っている様子を想像してみて下さい。
この場合、ビールケースが『集合体』で、ビール一本一本が『要素』になります。
もうちょっと、システム屋さんに身近な例で言うと、テーブルの中にレコードが一杯ありますよね。
その場合、テーブルが『集合体』で、レコードが『要素』になります。
上の例で挙げた、ビールケースとテーブルの『集合体』としての違いは、
ビールケースは 1 ケースの上限が 24 本で、テーブルは上限は決定してないに等しいですよね。(正確にはテーブルの場合は DBMS 依存)
上限があるものについては、配列で管理した方が効率的だし、
上限がないものに関しては System.Collections.ArrayList 等で管理した方が楽ちんです。
だけど、扱うモノによって『集合体』を使っている部分を、全部実装し直すのは大変だし。
そんな時に、有用なデザインパターンが Iterator パターンです。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

氏名とあだなを入力して、追加ボタンをクリックすると、チームのメンバーを追加します。
一覧表示をクリックすると、追加されたメンバーの一覧をリストボックスに表示します。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。
StartUpFormは、『要素』の追加と数え上げの窓口を担っていますが、
『集合体』(サンプルでは Team )の細かい仕様(上限があるとかないとか)は意識していません。
Public Class StartUpForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents TextBox1 As System.Windows.Forms.TextBox Friend WithEvents TextBox2 As System.Windows.Forms.TextBox Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents Button2 As System.Windows.Forms.Button Friend WithEvents ListBox1 As System.Windows.Forms.ListBox Friend WithEvents Label1 As System.Windows.Forms.Label Friend WithEvents Label2 As System.Windows.Forms.Label <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.TextBox1 = New System.Windows.Forms.TextBox Me.TextBox2 = New System.Windows.Forms.TextBox Me.Button1 = New System.Windows.Forms.Button Me.Button2 = New System.Windows.Forms.Button Me.ListBox1 = New System.Windows.Forms.ListBox Me.Label1 = New System.Windows.Forms.Label Me.Label2 = New System.Windows.Forms.Label Me.SuspendLayout() ' 'TextBox1 ' Me.TextBox1.Location = New System.Drawing.Point(72, 32) Me.TextBox1.Name = "TextBox1" Me.TextBox1.Size = New System.Drawing.Size(136, 19) Me.TextBox1.TabIndex = 0 Me.TextBox1.Text = "TextBox1" ' 'TextBox2 ' Me.TextBox2.Location = New System.Drawing.Point(72, 56) Me.TextBox2.Name = "TextBox2" Me.TextBox2.Size = New System.Drawing.Size(136, 19) Me.TextBox2.TabIndex = 1 Me.TextBox2.Text = "TextBox2" ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(208, 56) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 2 Me.Button1.Text = "Button1" ' 'Button2 ' Me.Button2.Location = New System.Drawing.Point(96, 88) Me.Button2.Name = "Button2" Me.Button2.TabIndex = 3 Me.Button2.Text = "Button2" ' 'ListBox1 ' Me.ListBox1.ItemHeight = 12 Me.ListBox1.Location = New System.Drawing.Point(32, 120) Me.ListBox1.Name = "ListBox1" Me.ListBox1.Size = New System.Drawing.Size(232, 112) Me.ListBox1.TabIndex = 4 ' 'Label1 ' Me.Label1.Location = New System.Drawing.Point(0, 32) Me.Label1.Name = "Label1" Me.Label1.TabIndex = 5 Me.Label1.Text = "Label1" ' 'Label2 ' Me.Label2.Location = New System.Drawing.Point(0, 56) Me.Label2.Name = "Label2" Me.Label2.TabIndex = 6 Me.Label2.Text = "Label2" ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.TextBox2) Me.Controls.Add(Me.Label2) Me.Controls.Add(Me.TextBox1) Me.Controls.Add(Me.Label1) Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.ListBox1) Me.Controls.Add(Me.Button1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region 'チーム Private m_myTeam As Team '画面がロードされた時のイベント Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Iterator" Me.TextBox1.Text = "" Me.TextBox2.Text = "" Me.Button1.Text = "追加" Me.Button2.Text = "一覧表示" Me.Label1.Text = "氏名" Me.Label2.Text = "あだな" Me.ListBox1.Items.Clear() '空のチームを作成する Me.m_myTeam = New Team End Sub '追加ボタンをクリックした時のイベント Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim addUser As User addUser = New User(Me.TextBox1.Text, Me.TextBox2.Text) Me.m_myTeam.AppendUser(addUser) 'メッセージの表示 MessageBox.Show(addUser.Name + "(" + addUser.NickName + ")" + "を追加しました", _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Information) Me.TextBox1.Text = "" Me.TextBox2.Text = "" Me.TextBox1.Focus() End Sub '一覧表示ボタンをクリックした時のイベント Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click Dim teamIterator As IIterator = Me.m_myTeam.CreateIterator() While (teamIterator.HasNext()) Dim objUser As User = DirectCast(teamIterator.SerchNext(), User) Me.ListBox1.Items.Add(objUser.Name + "(" + objUser.NickName + ")") End While End Sub End Class
『集合体』を表すインターフェースの ICollection です。
このインターフェースを implements する クラスは CreateIterator メソッドで
自身の持っている『要素』の数え上げを行ってくれる人( IIterator )を実装します。
'集合体を表すインタフェース Public Interface ICollection 'イテレータを作成するメソッド定義 Function CreateIterator() As IIterator End Interface
『集合体』の持っている要素を数え上げてくれるインターフェースの IIterator です。
'反復子インターフェース Public Interface IIterator '次の要素があるか調べる Function HasNext() As Boolean '次の要素を取得する Function SerchNext() As Object End Interface
今度は具体的な『集合体』です。人の集合→チームとしました。
メンバー数の上限は特に定めないので、 System.Collections.ArrayList で管理する事にしました。
'チーム Public Class Team Implements ICollection 'チームに所属する人達 Private m_users As ArrayList '今現在の人の位置 Private m_last As Integer 'コンストラクタ Public Sub New() Me.m_users = New ArrayList End Sub '位置を指定して要素を取得する Public Function GetUserAt(ByVal index As Integer) As User Return DirectCast(Me.m_users(index), User) End Function '人を追加する Public Sub AppendUser(ByVal addUser As User) Me.m_users.Add(addUser) '現在の人数に1人追加する Me.m_last += 1 End Sub '存在する人の数を取得する Public Function GetLength() As Integer Return Me.m_last End Function 'イテレータを作成するメソッド実装 Public Function CreateIterator() As IIterator Implements ICollection.CreateIterator Return New TeamIterator(Me) End Function End Class
チーム(集合体)に属する人達(要素)の数え上げを行うクラスです。
IIterator を実装しています。
'チームのイテレータ Public Class TeamIterator Implements IIterator 'チーム Private m_team As Team '位置 Private m_index As Integer 'コンストラクタ Public Sub New(ByVal tm As Team) Me.m_team = tm Me.m_index = 0 End Sub '次の要素があるか調べる Public Function HasNext() As Boolean Implements IIterator.HasNext If Me.m_index < Me.m_team.GetLength() Then Return True Else Return False End If End Function '次の要素を取得する Public Function SerchNext() As Object Implements IIterator.SerchNext Dim objUser As User = Me.m_team.GetUserAt(Me.m_index) Me.m_index += 1 Return objUser End Function End Class
チーム(集合体)に属する人達(要素)のクラスです。
'人 Public Class User '名前 Private m_name As String = "" 'あだ名 Private m_nickname As String = "" 'コンストラクタ Public Sub New(ByVal name As String, _ ByVal nickname As String) Me.m_name = name Me.m_nickname = nickname End Sub '名前アクセサ Public ReadOnly Property Name() As String Get Return Me.m_name End Get End Property 'あだ名アクセサ Public ReadOnly Property NickName() As String Get Return Me.m_nickname End Get End Property End Class
■ひとこと
上記サンプルで、もしチームは「サッカーチームにしよう」と思い、 ArrayList で管理するのをやめるとしたら?
Team クラスと、TeamIterator クラスを修正するだけで、とりあえずは済みそうですね。
Iterator パターンはデザインパターンの入り口として、書籍やインターネット上に、大変易しいサンプルが沢山あります。
参考にしてみてくださいね。