ExcelVBAのクラスモジュールの基本②

Excel
スポンサーリンク

前回はクラスの基本について書きました。そして後半では、クラス無しコードも記述して、クラスありと無しの場合を比較しました。

今回は、もっと掘り下げてクラスの使いどころについて検証していきたいと思います。

クラスの必要性をいまいち感じない、使いどころがわからないという人に読んでいただきたいです。

スポンサーリンク
スポンサーリンク

前回のコード(振り返り)

クラスありのコード

クラス有
車クラス

車クラスモジュールを作成して、Carオブジェクトを生成しました。

①車クラスにはタイヤプロパティとガソリンプロパティを作っています。

②イニシャライズでタイヤプロパティに50が代入されます。

③空気メソッド:タイヤプロパティに10が加算されるメソッドです。

④空気調整メソッド:タイヤプロパティからxが減算されるメソッドです。

⑤給油メソッド:ガソリンプロパティに10が加算されるメソッドです。

⑥排出メソッド:ガソリンプロパティからxが減算されるメソッドです。

⑦空気圧メソッド、⑧残量メソッドを作っています。ファンクション型になっており、返り値としてそれぞれタイヤプロパティ、ガソリンプロパティが設定されています。


Sub クラスTEST_1()

    Dim car As 車
    Set car = New 車
    
    
        car.空気
     
        car.空気調整 (60)
    
        car.給油
       
        car.排出 (0)
     
           
        MsgBox "タイヤの空気:" & car.空気圧結果
        MsgBox "のガソリンの残量:" & car.残量
       
End Sub



Private タイヤ As Long
Private ガソリン As Long


Private Sub Class_Initialize()
    タイヤ = 50
End Sub

Sub 空気()
    タイヤ = タイヤ + 10
End Sub

Sub 空気調整(x As Integer)
        タイヤ = タイヤ - x
End Sub

Sub 給油()
    ガソリン = ガソリン + 10
End Sub

Sub 排出(x As Integer)
    ガソリン = ガソリン - x
End Sub

Function 空気圧結果() As Integer
    空気圧結果 = タイヤ
End Function

Function 残量() As Integer
    残量 = ガソリン
End Function


クラス無しのコード


Private タイヤ As Long
Private ガソリン As Long

Sub クラスTEST_2()

       タイヤ = 50
    
        Call 空気
     
        Call 空気調整(60)
    
        Call 給油

        Call 排出(0)
     
        タイヤ = 空気圧結果
        ガソリン = 残量
         
        MsgBox "タイヤの空気:" & タイヤ
        MsgBox "のガソリンの残量:" & 残量
       
End Sub

Sub 空気()
    タイヤ = タイヤ + 10
End Sub

Sub 空気調整(x As Integer)
        タイヤ = タイヤ - x
End Sub

Sub 給油()
    ガソリン = ガソリン + 10
End Sub

Sub 排出(x As Integer)
    ガソリン = ガソリン - x
End Sub

Function 空気圧結果() As Integer
    空気圧結果 = タイヤ
End Function
Function 残量() As Integer
    残量 = ガソリン
End Function



前回も述べましたが、このコードを見比べて、そこまでクラスを使うメリットを感じないということ・・・。

では、どういう場合にクラスが有効なのかを具体的に検証します。

スポンサーリンク

今回のコード(クラス無し)

前回のコードを少し変更して、使用する変数を配列変数にしました。

最終的にはそれぞれの変数に格納してある数字をメッセージボックスに出しています。


Sub クラスTEST_2()

    Dim タイヤ(1 To 3) As Long
    Dim x As Integer
    Dim i As Integer
    j = 1
    For i = 1 To 3
         タイヤ(i) = 50
         Call 空気(タイヤ, j)
         x = x + 10
         Call 空気調整(タイヤ, x, j)
         MsgBox "車" & i & "のタイヤの空気:" & 空気圧結果(タイヤ, j)
         j = j + 1
    Next              
End Sub

Sub 空気(タイヤ() As Long, j As Integer)
    
    タイヤ(j) = タイヤ(j) + 10
    
End Sub

Sub 空気調整(タイヤ() As Long, ByVal x As Integer, j As Integer)
     
    タイヤ(j) = タイヤ(j) - x
     
End Sub

Function 空気圧結果(タイヤ() As Long, j As Integer) As Long
    空気圧結果 = タイヤ(j)
End Function


コードの流れ

  1. 配列変数を宣言
  2. For ~Nextステートメントで添え字を代入
  3. タイヤ配列変数に50を代入
  4. コールステートメントで空気調整プロシージャを呼び出し、タイヤ配列変数に10を加算
  5. コールステートメントで空気調整プロシージャを呼び出し、タイヤ変数から引数分減算
  6. プロシージャ名:空気圧結果のファンクションプロシージャを使ってメッセージボックスを表示する(ファンクションではなく変数を直接使っても良かったですが、後述するクラスのあるコードにファンクションを使っているのでそれに合わせています。)
スポンサーリンク

今回のコード(クラスあり)

前回と同様に車クラスを使います。(クラスの基本、使い方については前回の記事をご覧ください。)

標準モジュール


Sub クラスTEST_2()

    Dim Car(1 To 3) As 車
    
    Dim i As Long
    Dim x As Integer
    
    For i = 1 To 3
        Set Car(i) = New 車
        Car(i).空気
        x = x + 10
        Car(i).空気調整 x
         MsgBox "車" & i & "のタイヤの空気:" & Car(i).空気圧結果
    Next i
       
        
End Sub

車クラス


Public タイヤ As Long


Private Sub Class_Initialize()
    タイヤ = 50
End Sub

Sub 空気()
    タイヤ = タイヤ + 10
End Sub

Sub 空気調整(タイヤ As Integer)
        Me.タイヤ = Me.タイヤ - タイヤ
End Sub
Function 空気圧結果() As Integer
    空気圧結果 = タイヤ
End Function

  1. For~Nextステートメントを使って、車クラスのCarオブジェクトを3つ生成しています。Car(1)、Car(2)、Car(3)です。(イニシャライズを設定してあり、タイヤプロパティに50が代入されています)
  2. それぞれのCarオブジェクトの空気メソッドで10が加算
  3. 空気調整メソッドでそれぞれの引数をタイヤプロパティから減算
  4. 空気圧結果メソッドを使って、メッセージボックスを出しています

比較

比較するとそこまで大きくコードがすっきりしたという印象はないと思います。しかし、クラス無しの場合は、いちいち他のプロシージャに値を渡す時に引数で添え字(配列変数番号)を渡していますが、クラス有りの場合は、配列変数に使っている番号分だけオブジェクトを生成しているわけですから、そういった引数は省略できます。

つまり、クラス無しの場合は、他のプロシージャに渡す時に、どの配列番号なのかの指定が必須ですが、クラス有りの場合は必要ありません。

メリット

そもそもクラスは、複数のオブジェクトが同一のクラスを共有できるということが大きな特長でありメリットです。

今回で言えばCarオブジェクト(1)~(3)は同一の「車」クラスを扱えるということです。

全体図

クラスの中のプロパティとメソッドをオブジェクトごとに扱えることが最大のメリットです。

スポンサーリンク

配列とクラス

配列変数を使って、以下のコードのようにCar配列変数に車A、車B、車C配列変数を入れてあげることで車A、B、Cをクラスのプロパティのように扱うことが可能です。

しかし、配列変数の数が増えていくと、入力がかなり大変になってきます。

こういう場合もクラスを使った方が効果的ということがわかります。

クラス無し

'配列プロシージャ

Sub クラスTEST_3()

   Dim 車A(0 To 2) As Variant
   Dim 車B(0 To 2) As Variant
   Dim 車C(0 To 2) As Variant
   Dim car(0 To 2) As Variant
    
    車A(0) = 200
    車A(1) = 200
    車A(2) = "トラック"
    
    車B(0) = 50
    車B(1) = 50
    車B(2) = "軽自動車"
    
    車C(0) = 100
    車C(1) = 100
    車C(2) = "普通車"
    
    car(0) = 車A
    car(1) = 車B
    car(2) = 車C
    
    Call 調整(5, 5, car(0))
End Sub

Sub 調整(x As Integer, y As Integer, car As Variant)
    car(0) = car(0) + x
    car(1) = car(1) + y
End Sub
二次元配列の中身

調整プロシージャにCar(0)の(0)と(1)を渡して5を加算するというコードです。

クラスあり

'標準モジュール
Sub クラスTEST_5()

    Dim car(1 To 3) As 車
    
    Dim i As Long
    
    
    For i = 1 To 3
        Set car(i) = New 車
        car(i).サイズ Cells(i + 1, 1).Value, Cells(i + 1, 2).Value, Cells(i + 1, 3).Value
    Next i

    car(1).調整 5, 5

End Sub

'クラスモジュール

Public 重量 As Long
Public 車高 As Long
Public 車種 As String



Sub サイズ(重量 As Long, 車高 As Long, 車種 As String)
  Me.重量 = 重量
  Me.車高 = 車高
  Me.車種 = 車種
End Sub

Sub 調整(重量 As Long, 車高 As Long)
    Me.重量 = Me.重量 + 重量
    Me.車高 = Me.車高 + 車高
End Sub

ワークシート
クラスローカルウィンドウ

調整プロパティでCar(1)クラスオブジェクトの車高プロパティと重量プロパティに5が加算されます。

ワークシートに入力したものを引数として使用することでFor文で一気に取り込めます。

クラス無しの場合もワークシートを参照して、For文と二次元配列を使うことで一気に取り込めますが、二次元配列では、配列をクラスのオブジェクトとプロパティ、メソッドのように使うことはできづらいです。

'二次元配列
Sub クラスTEST_4()

   Dim r As Integer
   Dim c As Integer
   Dim car(1 To 3, 1 To 3) As Variant
   
    For r = 1 To 3
        For c = 1 To 3
        
        car(r, c) = Cells(r + 1, c).Value
        
        Next c
    Next r
   
End Sub
二次元配列のローカルウィンドウ
スポンサーリンク

検証結果:配列とクラスの比較(まとめ)

  1. クラスを使うことで、他のプロシージャに渡す時に配列の添え字を省略できる。
  2. 配列変数を複数使用する場合は、クラスにすることでコードのシンプル化ができる。
  3. オブジェクトごとのプロパティ、メソッドが活用できるので、他のプロシージャに渡す場合に配列より扱いやすい。
スポンサーリンク

プロパティのカプセル化

さて、前回の記事でクラスのプロパティをPrivateにしていることをお伝えしたと思います。

これについては、プロパティの値が標準プロシージャで簡単に変更されるのを防ぐ意味があります。

'標準プロシージャ
Sub クラスTEST_2()

    Dim car(1 To 3) As 車
    
    Dim i As Long
    Dim x As Integer
    
    For i = 1 To 3
        Set car(i) = New 車
        car(i).空気
        x = x + 10
        car(i).空気調整 x
         MsgBox "車" & i & "のタイヤの空気:" & car(i).空気圧結果
    Next i
   
   
    car(1).タイヤ = 0 'タイヤプロパティに0を代入
    MsgBox "車1のタイヤの空気:" & car(1).タイヤ
          
End Sub
'クラスモジュール

Public タイヤ As Long

Private Sub Class_Initialize()
    タイヤ = 50
End Sub

Sub 空気()
    タイヤ = タイヤ + 10
End Sub

Sub 空気調整(タイヤ As Integer)
    Me.タイヤ = Me.タイヤ - タイヤ
End Sub

Function 空気圧結果() As Integer
    空気圧結果 = タイヤ
End Function

クラスの中のタイヤプロパティはPublicです。

標準プロシージャ内でcar(1).タイヤ = 0ができてしまうとタイヤプロパティは一瞬で0になってしまいます。これだとせっかくクラス内でプロパティの値を保存していたとしても、簡単にリセットされてしまいます。

よって、標準プロシージャ内では、クラスのプロパティを扱えないようにするためにprivateにしておくのが一般的です。

これをカプセル化と言います。

ExcelVBA学習のおすすめの本5選
筆者オススメの「これは秀逸!」「これは1冊持っとくべき」と思えるExcelVBAの本を紹介しています。ExcelVBAの書籍選びで、どれを選んでよいか迷っておられる方に参考にしていただきたいです。
スポンサーリンク

まとめ

今回はクラスの使いどころに絞って記事を書きました。

次回もお楽しみに!

コメント

タイトルとURLをコピーしました