Visual Basic for Applications (VBA) でクラスを使用するのが適切なのはいつですか?
OOP をサポートするほとんどの言語にとって、開発の加速とバグの減少は共通の利点であると思います。しかし、VBA の場合、特定の基準はありますか?
Visual Basic for Applications (VBA) でクラスを使用するのが適切なのはいつですか?
OOP をサポートするほとんどの言語にとって、開発の加速とバグの減少は共通の利点であると思います。しかし、VBA の場合、特定の基準はありますか?
誰がコードを開発および保守するかによって異なります。小さなアドホック アプリをハッキングする典型的な "パワー ユーザー" マクロ ライターは、クラスの使用に混乱する可能性があります。しかし、本格的な開発の場合、クラスを使用する理由は他の言語と同じです。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
もう 1 つの例は、クラスを使用して 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 は範囲外になり、ワークシートは再び保護されます。
基準は他の言語と同じだと思います
いくつかのデータといくつかのメソッドを結び付け、オブジェクトの作成/終了時に何が起こるかを具体的に処理する必要がある場合は、クラスが理想的です
たとえば、フォームを開いたときに実行されるプロシージャがいくつかあり、そのうちの 1 つに時間がかかる場合、各段階の時間を測定したいと考えるかもしれません......
開始と停止のための明白な関数のメソッドを備えたストップウォッチ クラスを作成できます。次に、これまでの時間を取得してテキスト ファイルで報告する関数を追加し、時間を計るプロセスの名前を表す引数を使用できます。調査のために、最も遅いパフォーマンスのみをログに記録するロジックを作成できます。
次に、プログレス バー オブジェクトを追加して、それを開閉し、現在のアクションの名前をミリ秒単位で表示し、以前に保存されたレポートに基づいて推定残り時間を表示することができます。
もう 1 つの例として、Access のユーザー グループのゴミが気に入らない場合は、ログインとログアウトのメソッドと、グループ レベルのユーザー アクセス制御/監査/特定のアクションのログ/エラーの追跡などの機能を備えた独自の User クラスを作成できます。
もちろん、関連のない一連のメソッドと多くの変数の受け渡しを使用してこれを行うこともできますが、すべてをクラスにカプセル化する方が良いように思えます。
遅かれ早かれ VBA の限界に近づくことはありますが、VBA は非常に強力な言語であり、会社が VBA に結び付ければ、実際に優れた複雑なソリューションを得ることができます。
クラスは、より複雑な API 関数を扱う場合、特にデータ構造が必要な場合に非常に役立ちます。
たとえば、GetOpenFileName() および GetSaveFileName() 関数は、多くのメンバーを持つ OPENFILENAME 構造を取ります。それらすべてを利用する必要はないかもしれませんが、それらはそこにあり、初期化する必要があります。
構造体 (UDT) と API 関数の宣言を CfileDialog クラスにラップするのが好きです。Class_Initialize イベントは、構造体のメンバーの既定値を設定するため、クラスを使用するときは、(Property プロシージャを使用して) 変更するメンバーを設定するだけで済みます。フラグ定数は 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 アプリケーションで非常に役立つ方法の一例にすぎません。
特定の基準があるとは言いませんが、VBAコードでクラスを使用するのに便利な場所を実際に見つけたことはありません。私の考えでは、Officeアプリの既存のモデルと非常に密接に関連しているため、そのオブジェクトモデルの外部に抽象化を追加すると混乱が生じます。
それは、VBAでクラスに役立つ場所を見つけられなかった、またはクラスを使用して完全に役立つことを実行できなかったということではありません。その環境でそれらが役立つとは思ってもみませんでした。
さまざまなクライアントに出くわす多くの VBA プロジェクトで使用するコードの自己カプセル化パッケージを作成する場合は、クラスを使用します。
実際のクラスを使用せずに VBA コードを再利用することもできます。たとえば、呼び出された VBACode. 次の構文を使用して、任意のモジュール内の任意の関数またはサブルーチンにアクセスできます。
VBCode.mysub(param1, param2)
テンプレート/ドキュメントへの参照を作成すると (dll の場合と同様)、他のプロジェクトのコードを同じ方法で参照できます。
何かをする必要があるときにクラスを使用しますが、クラスが最も効果的です:) たとえば、イベントに応答 (またはインターセプト) する必要がある場合は、クラスが必要です。UDT (ユーザー定義型) を嫌う人もいますが、私は好きなので、平易な英語の自己文書化コードが必要な場合は UDT を使用します。Pharmacy.NCPDP は strPhrmNum よりもはるかに読みやすいです :) しかし、UDT には制限があるため、Pharmacy.NCPDP を設定して、他のすべてのプロパティを設定できるようにしたいとします。また、誤ってデータを変更できないようにしたいと考えています。次に、UDT などに読み取り専用プロパティがないため、クラスが必要です。
もう 1 つの考慮事項は、単純な読みやすさです。複雑なデータ構造を扱っている場合、Company.Owner.Phone.AreaCode を呼び出して、すべてが構造化されている場所を追跡するだけでよいことを知っておくと便利なことがよくあります。特に、あなたが去ってから2年後にそのコードベースを維持しなければならない人々のために:)
私自身の 2 セントは「Code With Purpose」です。理由なくクラスを使用しないでください。しかし、理由がある場合は、それを実行してください:)
データの再帰 (別名 BOM 処理) では、カスタム クラスが非常に役立ちます。クラス モジュールを使用せずに再帰関数を作成することはできますが、多くのデータの問題には効果的に対処できません。
(なぜ人々が VBA 用の BOM ライブラリ セットを売り出さないのか、私にはわかりません。XML ツールが違いを生んだのかもしれません。)
複数のフォームインスタンスはクラスの一般的なアプリケーションです (多くの自動化の問題はそうでなければ解決できません)。質問はカスタムクラスに関するものだと思います。
Microsoft Access を使用する場合でも、オブジェクト指向プログラミングを使用してソフトウェアを開発することは、一般的に優れた方法です。オブジェクトを疎結合できるようにすることで、将来のスケーラビリティが可能になり、多くの利点が得られます。これは基本的に、システム内のオブジェクトが相互に依存しなくなることを意味するため、リファクタリングがはるかに簡単になります。これは、クラス モジュールを使用した Access で実現できます。欠点は、VBA でクラスの継承またはポリモーフィズムを実行できないことです。最後に、クラスの使用に関する厳格なルールはなく、ベスト プラクティスのみです。ただし、アプリケーションが大きくなるにつれて、クラスを使用して保守する方が簡単になることに注意してください。
レコードセットやクエリ定義よりも便利な SQL ラッパー クラスをアクセスで定義できます。たとえば、別の関連テーブルの基準に基づいてテーブルを更新する場合、結合は使用できません。それを行うためにvba recorsetとquerydefを構築することもできますが、クラスを使用すると簡単です。また、アプリケーションには 2 つ以上のテーブルを必要とするいくつかの概念がある場合があります。そのためにはクラスを使用する方が良いかもしれません。たとえば、アプリケーションはインシデントを追跡します。インシデントには、いくつかのテーブルに保持されるいくつかの属性があります {ユーザーとその連絡先またはプロファイル、インシデントの説明。ステータス追跡; サポート担当者がインシデントに対応するのに役立つチェックリスト。返事 ...} 。関連するすべてのクエリと関係を追跡するには、oop が役立ちます。すべてのコーディングの代わりに Incident.Update(xxx) を実行できるのは安心です...
特にVB.NETを参照している場合、VBAの基準が他の言語と異なる理由がわかりません。