web-dev-qa-db-ja.com

VBAでクラスを使用する利点は何ですか?

Excel 2007でVBAプログラミングを行っており、すべてのデータシートを別のシートにコピーする1つのワークブックがあります。新しいシートにはいくつかのヘッダー行があり、それらがどこにあるのかを追跡したいので、常に単語を見つける必要はありません。

Excelドキュメントが開いている間、クラスを使用してそれらを実行し続けるのが最も簡単ですか?それとも、これが重くて扱いにくくなり、サブルーチンを使い続ける必要がありますか?クラスを使用する利点は何ですか?いくつかのオブジェクトがあるのではなく、列とシートの検証だけです。

31
Margareta

サブルーチンだけでなくクラスを使用する利点は、クラスがよりクリーンなコードを記述できるレベルの抽象化を作成することです。確かに、VBAでクラスを使用したことがない場合、学習曲線がありますが、それを理解するのに時間をかける価値があると確信しています。

クラスに切り替えるべき重要な指標の1つは、関数とサブルーチンにパラメーターを絶えず追加している場合です。この場合、ほとんどの場合、クラスを使用するのが最善です。

クラスの説明を 以前のStack Overflowの回答の1つ からコピーしました。


クラスを使用するとどのように役立つかを示す長い例を次に示します。この例は長くなりますが、オブジェクト指向プログラミングのいくつかの原則がコードのクリーンアップに実際にどのように役立つかを示します。

VBAエディターで、_Insert > Class Module_に移動します。 [プロパティ]ウィンドウ(デフォルトでは画面の左下)で、モジュールの名前をWorkLogItemに変更します。次のコードをクラスに追加します。

_Option Explicit

Private pTaskID As Long
Private pPersonName As String
Private pHoursWorked As Double

Public Property Get TaskID() As Long
    TaskID = pTaskID
End Property

Public Property Let TaskID(lTaskID As Long)
    pTaskID = lTaskID
End Property

Public Property Get PersonName() As String
    PersonName = pPersonName
End Property

Public Property Let PersonName(lPersonName As String)
    pPersonName = lPersonName
End Property

Public Property Get HoursWorked() As Double
    HoursWorked = pHoursWorked
End Property

Public Property Let HoursWorked(lHoursWorked As Double)
    pHoursWorked = lHoursWorked
End Property
_

上記のコードは、使用しているデータに固有の厳密に型指定されたオブジェクトを提供します。多次元配列を使用してデータを保存する場合、コードは次のようになります。arr(1,1)はID、arr(1,2)はPersonName、arr(1,3)はHoursWorkedです。その構文を使用すると、何が何であるかを知るのは困難です。オブジェクトをまだ配列にロードしていると仮定しますが、代わりに上記で作成したWorkLogItemを使用します。この名前は、arr(1).PersonNameを実行して人の名前を取得できます。これにより、コードがはるかに読みやすくなります。

この例で動き続けましょう。オブジェクトを配列に保存する代わりに、collectionを使用してみます。

次に、新しいクラスモジュールを追加し、ProcessWorkLogと呼びます。そこに次のコードを入れます:

_Option Explicit

Private pWorkLogItems As Collection

Public Property Get WorkLogItems() As Collection
    Set WorkLogItems = pWorkLogItems
End Property

Public Property Set WorkLogItems(lWorkLogItem As Collection)
    Set pWorkLogItems = lWorkLogItem
End Property

Function GetHoursWorked(strPersonName As String) As Double
    On Error GoTo Handle_Errors
    Dim wli As WorkLogItem
    Dim doubleTotal As Double
    doubleTotal = 0
    For Each wli In WorkLogItems
        If strPersonName = wli.PersonName Then
            doubleTotal = doubleTotal + wli.HoursWorked
        End If
    Next wli

Exit_Here:
    GetHoursWorked = doubleTotal
        Exit Function

Handle_Errors:
        'You will probably want to catch the error that will '
        'occur if WorkLogItems has not been set '
        Resume Exit_Here


End Function
_

上記のクラスは、WorkLogItemのコレクションで「何かをする」ために使用されます。最初に、作業時間の合計数をカウントするように設定しました。書いたコードをテストしましょう。新しいモジュールを作成します(今回はクラスモジュールではなく、「通常の」モジュールのみ)。モジュールに次のコードを貼り付けます。

_Option Explicit

Function PopulateArray() As Collection
    Dim clnWlis As Collection
    Dim wli As WorkLogItem
    'Put some data in the collection'
    Set clnWlis = New Collection

    Set wli = New WorkLogItem
    wli.TaskID = 1
    wli.PersonName = "Fred"
    wli.HoursWorked = 4.5
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 2
    wli.PersonName = "Sally"
    wli.HoursWorked = 3
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 3
    wli.PersonName = "Fred"
    wli.HoursWorked = 2.5
    clnWlis.Add wli

    Set PopulateArray = clnWlis
End Function

Sub TestGetHoursWorked()
    Dim pwl As ProcessWorkLog
    Dim arrWli() As WorkLogItem
    Set pwl = New ProcessWorkLog
    Set pwl.WorkLogItems = PopulateArray()
    Debug.Print pwl.GetHoursWorked("Fred")

End Sub
_

上記のコードでは、PopulateArray()は単にWorkLogItemのコレクションを作成します。実際のコードでは、Excelシートまたはデータオブジェクトを解析してコレクションまたは配列を埋めるためのクラスを作成できます。

TestGetHoursWorked()コードは、クラスがどのように使用されたかを単に示しています。 ProcessWorkLogがオブジェクトとしてインスタンス化されていることがわかります。インスタンス化された後、WorkLogItemのコレクションはpwlオブジェクトの一部になります。これはSet pwl.WorkLogItems = PopulateArray()行で確認できます。次に、コレクションWorkLogItemsに作用する関数を呼び出します。

なぜこれが役立つのですか?

データが変更され、新しいメソッドを追加するとします。 WorkLogItemHoursOnBreakのフィールドが含まれており、それを計算する新しいメソッドを追加するとします。

必要なことは、次のようにWorkLogItemにプロパティを追加することだけです:

_Private pHoursOnBreak As Double

Public Property Get HoursOnBreak() As Double
    HoursOnBreak = pHoursOnBreak
End Property

Public Property Let HoursOnBreak(lHoursOnBreak As Double)
    pHoursOnBreak = lHoursOnBreak
End Property
_

もちろん、コレクションを作成するためのメソッドを変更する必要があります(使用したサンプルメソッドはPopulateArray()でしたが、このために別のクラスを用意する必要があります)。次に、新しいメソッドをProcessWorkLogクラスに追加します。

_Function GetHoursOnBreak(strPersonName As String) As Double
     'Code to get hours on break
End Function
_

TestGetHoursWorked()メソッドを更新してGetHoursOnBreakの結果を返す場合、次の行を追加するだけで済みます。

_    Debug.Print pwl.GetHoursOnBreak("Fred")
_

データを表す値の配列を渡した場合は、配列を使用したコード内のすべての場所を見つけて、それに応じて更新する必要があります。代わりにクラス(およびそれらのインスタンス化されたオブジェクト)を使用すると、コードを簡単に更新して変更を処理できます。また、クラスを複数の方法で使用できるようにすると(おそらく、1つの関数に必要なオブジェクトプロパティは4つだけで、別の関数には6つ必要になります)、同じオブジェクトを参照できます。これにより、さまざまなタイプの関数に複数の配列を使用できなくなります。

さらに読むには、高くVBA Developer's Handbook、2nd edition のコピーを取得することをお勧めします。この本には、優れた例とベストプラクティス、大量のサンプルコードが満載です。深刻なプロジェクトのためにVBAに多くの時間を費やしているなら、この本を調べるのに十分な時間があるでしょう。

76
Ben McCormack

多数のサブルーチンがある場合、またはサブルーチンが非常に長い場合は、クラスにコードを構造化すると役立つ場合があります。たとえば、それぞれがわずか10行のコードであるサブルーチンが2つしかない場合は、これは過剰終了です。コードをクラスに構造化することの利点は、後から戻ってきたときに読みやすく、変更しやすいことです。そのため、コードをクラスに構造化するもう1つの理由は、コードを変更する必要がある可能性が高い場合です。

6
Carl Rippon

他の貢献者が述べた利点に追加できるものがもう1つあります(Ben McCormackの優れた答えのどこかにあり、それを見逃してしまった場合は申し訳ありません)。 VBAスクリプトが何らかの時点で再プログラムされる可能性が高い場合、クラスを使用できます。

例えば、私は一種の注文管理システムを設計しています。これはかなりの数の同僚によって使用されることになっていますが、順序付け規則が変更された場合は再プログラミングが必要になる場合があります。そのため、ストックアイテムに関するすべての情報を収集する基本的なストックアイテムクラスを設計しました。ただし、任意の順序でこのデータを分析する方法に関するルールは、簡単にアクセスでき、コメントの多いサブルーチンで記述されています。これを行うことで、将来のVBAプログラマーが、特定のストックアイテムに関するすべてのデータの収集方法に対処することなく、注文が生成される数学ルールを簡単に変更できることを願っています(これはすべて、クラス内のサブルーチンと関数によって行われます、クラスに在庫番号が渡されるとアクティブになります)。クラスのパブリックプロパティはインテリセンスによっても選択されるため、次のプログラマも自分自身も簡単に過ごすことができます。

ポイントは、クラスがこの方法で後のユーザーの生活を楽にすることができるということだと思いますif基本的な情報のセット、または常に概念のオブジェクトをエンコードします。プログラムの使用。

4
Orphid