web-dev-qa-db-ja.com

VBAでクラスを使用する場合

Visual Basic for Applications(VBA)でクラスを使用するのが適切なのはいつですか?

開発の加速とバグの導入の削減 は、OOPをサポートするほとんどの言語に共通の利点であることを想定しています。しかし、VBAでは、特定の基準がありますか?

39

それは、だれがコードを開発して保守するかによって異なります。小さなアドホックアプリをハッキングする一般的な「パワーユーザー」マクロライターは、クラスを使用すると混乱する可能性があります。しかし、本格的な開発では、クラスを使用する理由は他の言語と同じです。 VB6と同じ制限-継承なし-がありますが、インターフェイスを使用してポリモーフィズムを持つことができます。

クラスの適切な使用法は、エンティティおよびエンティティのコレクションを表すことです。たとえば、Excelの範囲を2次元配列にコピーし、次のようなコードで2次元配列を操作するVBAコードをよく目にします。

Total = 0
For i = 0 To NumRows-1
    Total = Total + (OrderArray(i,1) * OrderArray(i,3))
Next i

範囲を適切な名前のプロパティを持つオブジェクトのコレクションにコピーすると、次のように読みやすくなります。

Total = 0
For Each objOrder in colOrders
    Total = Total + objOrder.Quantity * objOrder.Price
Next i

別の例は、クラスを使用してRAII設計パターンを実装することです(そのためのGoogle)。たとえば、ワークシートの保護を解除し、いくつかの操作を行ってから、もう一度保護する必要があるかもしれません。クラスを使用すると、コードでエラーが発生した場合でも、ワークシートが常に保護されます。

--- WorksheetProtector class module ---

Private m_objWorksheet As Worksheet
Private m_sPassword As String

Public Sub Unprotect(Worksheet As Worksheet, Password As String)
    ' Nothing to do if we didn't define a password for the worksheet
    If Len(Password) = 0 Then Exit Sub

    ' If the worksheet is already unprotected, nothing to do
    If Not Worksheet.ProtectContents Then Exit Sub

    ' Unprotect the worksheet
    Worksheet.Unprotect Password

    ' Remember the worksheet and password so we can protect again
    Set m_objWorksheet = Worksheet
    m_sPassword = Password
End Sub

Public Sub Protect()
    ' Protects the worksheet with the same password used to unprotect it
    If m_objWorksheet Is Nothing Then Exit Sub
    If Len(m_sPassword) = 0 Then Exit Sub

    ' If the worksheet is already protected, nothing to do
    If m_objWorksheet.ProtectContents Then Exit Sub

    m_objWorksheet.Protect m_sPassword
    Set m_objWorksheet = Nothing
    m_sPassword = ""
End Sub

Private Sub Class_Terminate()
    ' Reprotect the worksheet when this object goes out of scope
    On Error Resume Next
    Protect
End Sub

次に、これを使用してコードを簡略化できます。

Public Sub DoSomething()
   Dim objWorksheetProtector as WorksheetProtector
   Set objWorksheetProtector = New WorksheetProtector
   objWorksheetProtector.Unprotect myWorksheet, myPassword

   ... manipulate myWorksheet - may raise an error

End Sub 

このSubが終了すると、objWorksheetProtectorはスコープ外になり、ワークシートは再び保護されます。

50
Joe

基準は他の言語と同じだと思います

複数のデータといくつかのメソッドを結び付ける必要があり、オブジェクトが作成/終了されたときに何が起こるかを具体的に処理する必要がある場合は、クラスが理想的です

フォームを開いたときに実行されるいくつかのプロシージャがあり、そのうちの1つに長い時間がかかっている場合、各ステージの時間を計ることができます。

開始と停止の明らかな関数のメソッドを備えたストップウォッチクラスを作成し、時間を取得する関数を追加して、時間を計測しているプロセスの名前を表す引数を使用して、それをテキストファイルに報告することができます。調査のために最も遅いパフォーマンスのみをログに記録するロジックを作成できます。

次に、プログレスバーオブジェクトを追加して、オブジェクトを開いたり閉じたり、現在のアクションの名前を、ミリ秒単位の時間や、以前に保存されたレポートに基づいて推定される残り時間などと共に表示したりできます。

別の例として、Accessのユーザーグループのごみが気に入らない場合、ログインおよびログアウトのメソッドと、グループレベルのユーザーアクセス制御/監査/特定のアクションのログ/エラーの追跡などの機能を備えた独自のUserクラスを作成できます。

もちろん、関係のない一連のメソッドと多くの変数の受け渡しを使用してこれを行うことができますが、それをすべてクラスにカプセル化する方が私にはちょうど良いように思えます。

あなたは遅かれ早かれVBAの限界に近づきますが、それは非常に強力な言語であり、あなたの会社がそれに縛られている場合、実際にいくつかの優れた複雑なソリューションを得ることができます。

8
ajp

クラスは、より複雑なAPI関数を扱う場合、特にデータ構造が必要な場合に非常に役立ちます。

たとえば、GetOpenFileName()関数とGetSaveFileName()関数は、多数のメンバーを持つOPENFILENAME構造体を取ります。それらのすべてを利用する必要はないかもしれませんが、それらはそこにあり、初期化する必要があります。

構造(UDT)とAPI関数宣言をCfileDialogクラスにラップするのが好きです。 Class_Initializeイベントは、構造体のメンバーのデフォルト値を設定するため、クラスを使用するときに、変更したいメンバーを(プロパティプロシージャを使用して)設定するだけで済みます。フラグ定数はEnumとして実装されます。したがって、たとえば、開くスプレッドシートを選択する場合、コードは次のようになります。

Dim strFileName As String
Dim dlgXLS As New CFileDialog

With dlgXLS
  .Title = "Choose a Spreadsheet"
  .Filter = "Excel (*.xls)|*.xls|All Files (*.*)|*.*"
  .Flags = ofnFileMustExist OR ofnExplorer

  If OpenFileDialog() Then
    strFileName = .FileName
  End If
End With
Set dlgXLS = Nothing

クラスはデフォルトのディレクトリをMy Documentsに設定しますが、必要に応じてInitDirプロパティで変更できます。

これは、クラスがVBAアプリケーションで非常に有益になる例の1つにすぎません。

5
Mike

特定の基準があるとは言いませんが、VBAコードでクラスを使用するための便利な場所を実際に見つけたことはありません。私の考えでは、Officeアプリを取り巻く既存のモデルと非常に関連しているため、オブジェクトモデルの外部に抽象化を追加すると、混乱するだけです。

could n't VBAでクラスの便利な場所を見つけたり、クラスを使用して完全に便利なことをしたりするわけではありません。

5
cori

実際のクラスを使用せずにVBAコードを再利用することもできます。たとえば、呼び出された場合、VBACode。次の構文を使用して、任意のモジュール内の任意の関数またはサブルーチンにアクセスできます。

VBCode.mysub(param1, param2)

(dllの場合と同様に)テンプレート/ドキュメントへの参照を作成すると、同じ方法で他のプロジェクトのコードを参照できます。

3
JonnyGold

Microsoft Accessを使用していても、オブジェクト指向プログラミングを使用してソフトウェアを開発することは、一般的には良い習慣です。オブジェクトを疎結合できるようにすることで、将来的にスケーラビリティが可能になるだけでなく、多くの利点があります。これは基本的に、システム内のオブジェクトの相互依存度が低くなるため、リファクタリングがはるかに簡単になることを意味します。クラスモジュールを使用してこれを実現できます。欠点は、VBAでクラス継承またはポリモーフィズムを実行できないことです。結局のところ、クラスを使用することに関する厳格な規則はなく、ベストプラクティスのみです。ただし、アプリケーションが成長するにつれて、クラスの使用を維持するのが容易になることを覚えておいてください。

2
jinsungy

データの再帰(別名BOM処理)の場合、カスタムクラスは非常に役立ち、不可欠な場合もあります。クラスモジュールなしで再帰関数を作成できますが、多くのデータの問題に効果的に対処できません。

(なぜVBAのBOMライブラリセットを売り出していないのかはわかりません。たぶんXMLツールが違いを生んだのでしょう。)

複数のフォームインスタンスは、クラスの一般的なアプリケーションです(自動化の問題の多くは解決できません)。質問はカスタムクラスに関するものだと思います。

2
Smandoli

さまざまなクライアントのために出くわす多くのVBAプロジェクトで使用するコードの自己カプセル化パッケージを作成する場合は、クラスを使用します。

2
Jason TEPOORTEN

私は何かをする必要があるときにクラスを使用します。クラスはそれを最もうまく行います:)たとえば、イベントに応答(またはインターセプト)する必要がある場合は、クラスが必要です。 UDT(ユーザー定義型)が嫌いな人もいますが、私はそれが好きなので、単純な自己文書化コードが必要な場合に使用します。 Pharmacy.NCPDPはstrPhrmNumに比べてはるかに読みやすい:)しかし、UDTは制限されているため、Pharmacy.NCPDPを設定し、他のすべてのプロパティを設定できるようにしたいとします。また、誤ってデータを変更しないようにしたいと考えています。次に、UDTなどに読み取り専用のプロパティがないため、クラスが必要です。

別の考慮事項は、単純な読みやすさです。複雑なデータ構造を実行している場合、Company.Owner.Phone.AreaCodeを呼び出してすべてが構造化されている場所を追跡する必要があることを知っていると、多くの場合有益です。特に、あなたが去ってから2年後にそのコードベースを維持しなければならない人々のために:)

私自身の2セントは「目的のあるコード」です。理由のないクラスを使用しないでください。しかし、理由がある場合はそれを行ってください:)

2
Oorang

レコードセットやクエリ定義よりも便利なアクセスでSQLラッパークラスを定義できます。たとえば、別の関連テーブルの基準に基づいてテーブルを更新する場合、結合は使用できません。あなたはそれを行うためにvba recorsetとquerydefを構築することができますが、私はクラスでそれをより簡単に見つけます。また、アプリケーションには、2つ以上のテーブルを必要とするいくつかの概念がある場合があります。そのためにクラスを使用する方がいいかもしれません。例えば。アプリケーションはインシデントを追跡します。インシデントには、いくつかのテーブルに保持されるいくつかの属性があります{ユーザーとその連絡先またはプロファイル、インシデントの説明;ステータス追跡;サポート担当者がインシデントに返信するのに役立つチェックリスト。返信...}関連するすべてのクエリと関係を追跡するには、oopが役立ちます。すべてのコーディングの代わりにIncident.Update(xxx)を実行できるのは安心です...

0
Bellow Akambi