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というのは、

akashi-keirin.hatenablog.com

リスト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を渡して、要素を順に取り出しているだけ。

実行結果

f:id:akashi_keirin:20180310100630j:plain

いちおう、意図どおりの出力が得られている。

おわりに

「だから何?」とか言わないでください。

自分でも何の役に立つのか分からないし、これをどうしたら良いのかも分かりません。

これでIteratorになっているのかどうかも不明。

達人のアドヴァイス、お待ちしております。

デザインパターン」シリーズ、この後も続くのだろうか……???

@akashi_keirin on Twitter