Iteratorクラスを作ってみた
Iteratorクラスを作ってみた
デザインパターンをまじめに勉強したくなったので、遊び半分でコーディング。
Iteratorクラス
オブジェクト名は「Iterator」です。
リスト1ー1 クラスモジュール 宣言セクション
Option Explicit Private Const ERROR_NOT_INITIALIZED As String = _ "Iteratorクラスのinitメソッド未実行" Private cnt As Long Private isInitialized As Boolean Private vault_ As Variant Private countOf_ As Long Private hasNext_ As Boolean Private hasPrevious_ As Boolean
リスト1ー2 クラスモジュール Property部
Public Property Get hasNext() As Boolean If Not isInitialized Then Call raiseErrorNotInitialized hasNext = hasNext_ End Property Public Property Get getNext() As Variant If Not isInitialized Then Call raiseErrorNotInitialized If Not hasNext_ Then Exit Property If VarType(vault_.Item(cnt)) = vbObject Then Set getNext = vault_.Item(cnt) Else getNext = vault_.Item(cnt) End If cnt = cnt + 1 If cnt > countOf_ Then cnt = countOf_: hasNext_ = False End Property Public Property Get hasPrevious() As Boolean If Not isInitialized Then Call raiseErrorNotInitialized If cnt > 1 Then hasPrevious_ = True hasPrevious = hasPrevious_ End Property Public Property Get getPrevious() As Variant If Not isInitialized Then Call raiseErrorNotInitialized If Not hasPrevious_ Then Exit Property If VarType(vault_.Item(cnt)) = vbObject Then Set getPrevious = vault_.Item(cnt) Else getPrevious = vault_.Item(cnt) End If cnt = cnt - 1 If cnt < 1 Then cnt = 1: hasPrevious_ = False End Property
メソッドにすべきか迷ったが、せっかくPropertyという便利な仕組みがあるのだから、本家Iteratorのhasnext、nextいづれもPropertyにした。
ただし、「next」は予約語で使えないので、「getNext」にしてある。
リスト1ー3 クラスモジュール Constructor部
Private Sub Class_Initialize() cnt = 1 hasNext_ = True hasPrevious_ = False End Sub Public Function init(ByRef object_ As Variant) As Boolean On Error GoTo errorHandler If IsArray(object_) Then vault_ = object_ Set vault_ = convertArrayToCollection(object_) countOf_ = vault_.Count init = True isInitialized = True Exit Function End If If TypeName(object_) = "Collection" Then Set vault_ = object_ countOf_ = vault_.Count init = True isInitialized = True Exit Function End If If TypeName(object_) = "Dictionary" Then Set vault_ = convertArrayToCollection(object_.Items) countOf_ = vault_.Count init = True isInitialized = True Exit Function End If errorHandler: init = False isInitialized = False End Function
例によって、VBAはコンストラクタに引数を渡せないクソ仕様なので、コンストラクタが2段階になる。
2段階目のinitメソッドでは、受け取ったものが配列だろうがCollectionだろうがDictionaryだろうが、全てCollectionに突っ込むという処理を行っている。
ちなみに、convertArrayToCollectionというのは、
のリスト1です。
リスト1ー4 クラスモジュール その他
Private Sub raiseErrorNotInitialized() Err.Raise Number:=10001, _ description:=ERROR_NOT_INITIALIZED End Sub
2段階目のコンストラクタ未実行のままPropertyを参照したらエラーを吐くようにするためのメソッド。インスタンスから呼ばれたら困るからPrivate指定。
使ってみる
下記のコードで実行。
リスト2 標準モジュール
Public Sub hoge() Dim ar(2) As String ar(0) = "アホ" ar(1) = "ボケ" ar(2) = "カス" Dim dic As New Scripting.Dictionary With dic .Add Key:="a1", _ Item:="ち~んw" .Add Key:="a2", _ Item:="プヒー!" .Add Key:="a3", _ Item:="( ´,_ゝ`)プッ" .Add Key:="a4", _ Item:="( ´_ゝ`)フーン" End With Dim cl As New Collection With cl .Add "バカ" .Add "クズ" .Add "デコスケ" End With Dim it1 As New Iterator Call it1.init(ar) Do While it1.hasNext Debug.Print it1.getNext Loop Dim it2 As New Iterator Call it2.init(dic) Do While it2.hasNext Debug.Print it2.getNext Loop Dim it3 As New Iterator Call it3.init(cl) Do While it3.hasNext Debug.Print it3.getNext Loop Debug.Print it1.hasNext Debug.Print it2.hasNext Debug.Print it3.hasNext Debug.Print it1.hasPrevious Debug.Print it2.hasPrevious Debug.Print it3.hasPrevious Do While it1.hasPrevious Debug.Print it1.getPrevious Loop End Sub
無駄にタテに長いコードだが、勘弁してほしい。
参照設定で「Microsoft Scripting Runtime」にチェックを入れるのを忘れないようにしてください。
配列、Scripting.Dictionary、Collectionを渡して、要素を順に取り出しているだけ。
実行結果
いちおう、意図どおりの出力が得られている。
おわりに
「だから何?」とか言わないでください。
自分でも何の役に立つのか分からないし、これをどうしたら良いのかも分かりません。
これでIteratorになっているのかどうかも不明。
達人のアドヴァイス、お待ちしております。
「デザインパターン」シリーズ、この後も続くのだろうか……???