使って覚えるクラス

中級VBA

VBAでのクラスの基本的な使い方

 クラスを使うと効率的なプログラムを書く事が出来ます。
 クラスはオブジェクト指向を実現するための機構で、ある機能に対する変数や関数をグループ化して使う事が出来ます。
 こうする事で、後から変更したり、コードが見やすくなったりします。
 また、プログラマーもクラスを設計する際に問題を整理することとなり、どのようにアプローチすれば良いか考えがまとまりやすくなります。

実例でクラスを学ぶ

 色々と説明するよりも、まず基本的なクラスを作り、使ってみた方がイメージが分かり易いと思います。
 以前、クラスについての記事を書きましたが、今回は実例を挙げてから解説します。
 詳細はそちらのページを参照してください。

 VBAで「オブジェクト」という言葉をよく耳にすると思います。
 オブジェクトとはVBAではセル、シート、ブックなどの部品を指します。
 これらの実態はクラスなので、オブジェクト=クラスと考えても良いと思います。
 普段我々は、VBAの出来上がったクラスを使っていると言えます。
 
 クラスとは、変数と関数をグループ化して特別な変数のように扱えるものと捉えて良いと思います。
 これは私の印象なのですが、クラスは様々な設定値(プロパティ値)を保持しつつ、それらを使い回すことができます。

クラスの作り方

 今回は「車」をクラスと捉えて簡単なクラスを作り、使ってみたいと思います。
 と言っても複雑なものではありません。
 
 まず、クラスは標準モジュールではなく、クラスモジュールに書きます。
 プロジェクトウインドウから右クリックから「挿入」とすると「クラスモジュール」がありますのでこれをクリックしてください。
 
 出来上がったクラスは「Class1」というクラス名になっていますので、適当な名前に変更します。
 今回は「CCar」とします。最初の C はクラスの C を示しています。

 最初に実例を示します。

'/// CCar クラス (クラスモジュールに書きます。)///
 
'メンバ変数
Public mOwner As String

Private mColorNum As Long
Private mColor As String

Private mRange As Range

'初期化
Private Sub Class_Initialize()
    mTypeName = ""
    mOwner = "no Owner"
    mColorNum = 0
    mColor = ""
    Set mRange = Nothing

End Sub

Public Property Let Color(n As Long)
    mColorNum = n
    
    '他のメンバ変数に値を代入できる
    If n = 3 Then
        mColor = "Red"
    ElseIf n = 33 Then
        mColor = "Blue"
    Else
        mColor = "---"
    End If
    
End Property

Public Property Get Color() As Long
    Color = mColorNum
End Property


Public Property Set MyRange(r As Range)
    Set mRange = r
End Property

Public Property Get MyRange() As Range
    Set MyRange = mRange
End Property

'メソッド
Public Sub startPos()
    mRange.Value = mOwner
    mRange.Interior.ColorIndex = mColorNum
    
End Sub

Public Sub Forward(n As Long)
    If n <= 0 Then Exit Sub

    If n > 1 Then
        Dim i As Long
        For i = 1 To n - 1
            mRange.Offset(0, i).Value = "- - -"
        Next i
    End If
    mRange.Interior.ColorIndex = 0 ' 背景色 塗りつぶしなし
    mRange.Value = "● - -"
    
    Set mRange = mRange.Offset(0, n)
    
    mRange.Interior.ColorIndex = mColorNum
    mRange.Value = mOwner
    
End Sub

 クラスは、メンバ変数プロパティメソッドという3つに分けて書いていくと見やすいと思います。

メンバ変数

 クラス内で使う変数を「メンバ変数」と呼びます。それぞれ属性を付けられるのですが、Privateにして外から勝手に変更できないようにするのが普通です。
 今回は、「外から勝手に変更できる」のを体験するためにPublic属性のメンバ変数を1つ作りました。

プロパティ

 プロパティとは、外からメンバ変数にアクセスするための仕組みです。
 普段書きなれないと、回りくどいように感じるかもしれませんが、こうする事でクラスと外部とのやり取りを制限する事ができ強固なプログラムがかけるようになります。
 入力にはLet出力はGetを使います。両方とも同じ名前のプロパティとすれば、変数のように入力や出力が可能です。
 また、Letの時に別のメンバ変数への代入を行うなど高度な使い方もできます。
 
 注意が必要なのはVBAのオブジェクトを使うときです。
 この時は、Letの代わりにSetを使います。また、内部の代入の際にもSetが必要です
 外部で代入するときもこれまでと同じようにSetが必要になります

メソッド

 メソッドには属性(PrivatePublic)を付ける事が出来ます。
 こうする事で、クラス内部のみで使いたいメソッドとクラスの外部から呼び出したいメソッドを色分けできます。

クラスの使い方

 これまでの説明はクラスの設計方法でした。
 実際にクラスを使うには、設計図から実体化する必要があります。
 また、実体化したクラスに名前を付けて扱えるようにクラス型の変数を用意する必要があります。

 例を示します。

'/// このコードは、標準モジュールに書きます。 ////
Sub TestClass()

    Dim myCar As CCar
    Set myCar = New CCar
    
    'シートをクリア(以前の記録を消す)
    Range("A1:AA20").Interior.ColorIndex = 0
    Range("A1:AA20").ClearContents
    
    'プロパティ
    myCar.mOwner = "Tom"
    myCar.Color = 33
    Set myCar.MyRange = Range("C3")
    
    'メソッド
    myCar.startPos
    
    myCar.Forward 5
    
End Sub

 クラス型の変数を宣言したら、Set 変数名 = New クラス名 とすることで実体化します。
 実体化することを「インスタンス化」と言います。
 これは、設計図からPCのメモリ上に展開させる事になります。
 
 こすることで、クラスが使用できるようになしました。
 クラス内部の変数(メンバ変数)にアクセスするにはプロパティ値に設定すればOKです。
 この書き方はVBAのオブジェクトと同じ書き方ですね。
 
 メソッドも同じように クラスインスタンス.メソッド という書き方で実行できます。

クラスを使わない書き方

 これまでのクラスを使ったコードをクラスを使わない書き方にするとどうなるでしょうか?
 2つを比べて利点や欠点を見てみましょう。

'/// このコードは、標準モジュールに書きます。///
Sub TestCode()

    'シートをクリア(以前の記録を消す)
    Range("A1:AA20").Interior.ColorIndex = 0
    Range("A1:AA20").ClearContents
    
    '変数前言
    Dim Owner As String
    Dim nColor As Long
    Dim aRange As Range
    
    '変数初期化
    Owner = "Tom"
    nColor = 33
    Set aRange = Range("C3")
    
    'メソッド
    aRange.Value = Owner
    aRange.Interior.ColorIndex = nColor
    
    Stop
    
    Call goForward(5, aRange, nColor, Owner)
     
End Sub

Public Sub goForward(n As Long, rng As Range, myColor As Long, own As String)
    If n <= 0 Then Exit Sub

    If n > 1 Then
        Dim i As Long
        For i = 1 To n - 1
            rng.Offset(0, i).Value = "- - -"
        Next i
    End If
    rng.Interior.ColorIndex = 0 ' 背景色 塗りつぶしなし
    rng.Value = "● - -"
    
    Set rng = rng.Offset(0, n)
    
    rng.Interior.ColorIndex = myColor
    rng.Value = own
    
End Sub

 小さなコードなので、クラスの恩恵は無いように思えます。
 クラスを使わない場合、プロパティなどのコードは不要ですので短く書けています。
 ですので、小さな規模ではクラスにする必要性は無いようです。
 
 クラスの利点としては、変数の宣言がクラスのみでOKだという事です。
 今回のコードでは、メンバ変数にあたる変数を全て関数に押し込んでいます。
 これを関数の外に出してグローバル変数として使う場合は、管理が厄介になる事が予想されます。
 
 また、最後の関数(goForward)は外に出して呼び出していますが、変数を全て引数として渡さなければならず煩雑になってしまいます。クラスを用いた例では、 myCar.Forward 5 としたので非常に分かり易い書き方が出来ています。
 
 今回の例ではあまり感じませんが、クラスを使った書き方ですと、For文やIf文などが少なく、整然としたコードになるケースが多いです。
 そのため、プログラムの順序がイメージしやすく、大きな流れはクラスの外で、小さな流れはクラス内部で書くような形になってきます。

まとめ

 クラスを使った書き方というのは、最初から最後まで一様に流れていくような処理には不向きかもしれません。
 具体的には、転記を伴う集計処理などです。
 
 反対に変数にしなければならない要素が多く、それらを臨機応変に組み合わせていくような処理にはクラスが向いています。
 具体的にはゲームプログラムなどです。
 
 クラスを使ったオブジェクト指向とは、想定される処理をあらかじめ用意しておいて、それを組み合わせていくというようなやり方になります。そのため、後から仕様が変更になったり、機能を追加、修正する事になっても変更が楽で最初から作り直すというような事にはならずに済みます。
 
 また、クラス内の変数(メンバ変数)を外部から隔離して隠蔽できることからバグを防ぐ書き方ができるのもクラスの利点と言えます。

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