はじめに
この記事では、クラスの「動作」を担うメソッドについて説明します。
VBAでクラスを活用するうえで、メソッドの設計は極めて重要です。
特に Public(公開) と Private(非公開) の使い分けが、クラスの「使いやすさ」や「修正や拡張のしやすさ」を大きく左右します。
VBAクラスにおけるメソッドとは
メソッドとは、クラス内で定義する Sub または Function のことです。標準モジュールに作成する自作関数と構文は同じですが、クラスでは意味や使い方に違いがあります。
クラスが保持するデータに対して、どういう「処理」を提供するかを定義するのがメソッドです。
クラスのプロパティが「クラスの状態」を表すのに対して、メソッドはクラス内部に保持されたデータに対する「操作(例えば、データを追加・更新・削除するなど)」を担当します。
命名には、動作を示す動詞をメソッド名の先頭に使うのが一般的です。
例:
・CalculateTotal
・PrintInfo
・LoadFromSheet
PublicメソッドとPrivateメソッドの違い
メソッドは Public または Private のいずれかで定義できます。この指定によって、クラスの外部から呼び出せるかどうかが決まります。
なお、PublicとPrivateのどちらも指定しなかった場合は暗黙でPublicとして扱われます。ただ、指定せずに暗黙のPublicにするのはやめましょう。
というのも、第三者が見たときに「Publicの書き忘れなのか? それとも意図的に暗黙のPublicにしているのか?」をコードから判断しなければなりません。そのため、将来クラスを拡張する際に、余計な確認の手間がかかってしまいます。このような弊害があるため、PublicまたはPrivateは必ず記述するようにしましょう。
Publicメソッド
Publicメソッドは外部から呼び出せるメソッドです。クラス利用者に対して提供する「サービス」的な役割を持ちます。
Publicにするメソッドは、「公開する必要に迫られている場合」のみ公開します。
ちょっとおかしな例ですが、こちらの質問に対して素直に回答を返してくれる人と、質問とは関係ない回答や聞いてもいないことをバンバン話してくる人と、どちらが話しやすいでしょうか。当然前者です。
クラスのメソッドの設計も同様で、外部から「クラス内部のデータはこのように扱いたい」という要望を受け付ける窓口としてPublicメソッドを用意します。外部から使われないものはPublicメソッドにするべきではありません。
例:
1 2 3 |
Public Sub LoadData() '// 外部から呼ばれる処理 End Sub |
Privateメソッド
Privateメソッドはクラス内部でのみ使用するメソッドです。複雑な処理を分割して整理したり、再利用性を高めたりするために使います。
通常なPublicメソッドから呼び出される内部処理として利用します。
例:
1 2 3 |
Private Function IsValidAge() As Boolean IsValidAge = (pAge >= 18 And pAge <= 65) End Function |
実例:StringListクラスのメソッド設計
実際にStringListというクラスを例に、メソッドの設計でのPublic(公開)とPrivate(非公開)について説明します。
StringListクラスは文字列の配列を使いやすくしたクラスです。プログラミング経験者であれば、JavaのArrayListや、C#のList
クラス内に保持するデータとして、文字列のコレクションを用意し、Publicメソッドで文字列コレクションの操作を行っています。
以下のコードは簡易版ですが、VBAでArrayListのように使えるコードを「VBAでArrayListクラス」ページで紹介していますので興味があればご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
'// StringList.cls Option Explicit '// 文字列コレクション Private items As Collection '// クラス初期処理 Private Sub Class_Initialize() '// 文字列コレクションの領域確保 Set items = New Collection End Sub '// 文字列コレクションに文字列を追加 Public Sub Add(ByVal value As String) items.Add value '// デバッグ目的で内部状態を出力(本番環境では省略可) Call DebugInternalState End Sub '// 文字列コレクションに格納されているデータ数を返す Public Property Get Count() As Long Count = items.Count End Property '// 文字列コレクションから指定インデックスの文字列を返す Public Function GetItem(ByVal index As Long) As String '// 指定インデックスが文字列コレクションの範囲内の場合(データが存在する場合) If index >= 1 And index <= items.Count Then '// 指定インデックスの文字列を返す GetItem = items(index) Else '// エラー Err.Raise vbObjectError + 100, , "インデックスが範囲外です。" End If End Function '// 文字列コレクションの全要素をDebug.Printで表示 Public Sub ShowAll() Dim v As Variant For Each v In items Debug.Print v Next End Sub '// 内部状態を出力する非公開メソッド Private Sub DebugInternalState() Debug.Print "要素数: " & items.Count End Sub |
コード説明
- Add: 文字列コレクションに1つ追加します。
- GetItem(index): 指定位置の要素を取得(インデックスは 1 始まり)。
- Count: 要素数を返します。
- ShowAll: 文字列コレクションの全件出力(Debug.Print)。
- DebugInternalState: クラス内部でのみ使える Private メソッドです。
このクラスは、C# で言えば List
StringListクラスの使用例(標準モジュール)
StringListのPublicメソッドを順に使ったサンプルコードです。Add()でデータを追加し、そのあとに要素数の出力、2番目の要素の取得を行い、最後にリストの全件を出力しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Sub TestStringList() '// StringListの領域を確保 Dim list As New StringList '// リストに追加 list.Add "Apple" list.Add "Banana" list.Add "Cherry" Debug.Print "要素数: " & list.Count Debug.Print "2番目の要素: " & list.GetItem(2) '// リストの全件を表示 list.ShowAll End Sub |
実行結果(イミディエイトウィンドウに出力されます)
要素数: 1
要素数: 2
要素数: 3
要素数: 3
2番目の要素: Banana
Apple
Banana
Cherry
役割を分けて、テストしやすくする
良いメソッド設計の原則の1つに「1メソッド=1つの処理」があります。処理が長くなっている場合は、内部の手続きを Private メソッドに分割しましょう。
また、メソッド名と掛け離れた処理を行うのも、避けましょう。
例:
pName や pAge はクラスのメンバー変数(Private変数)だと仮定しています。クラス内でフォーム入力結果などを一時的に保持している想定です。
1 2 3 4 5 6 7 8 9 10 11 12 |
Public Sub SaveToSheet(sheet As Worksheet) If Not IsValidAge() Then Exit Sub End If sheet.Range("A2").Value = pName sheet.Range("B2").Value = pAge End Sub Private Function IsValidAge() As Boolean IsValidAge = (pAge >= 18 And pAge <= 65) End Function |
IsValidAge を分けておくことで、例えば単体テスト時に年齢の条件だけを集中的にテストすることができ、意図しない副作用を防ぎやすくなります。
このようにPublicメソッドの中の処理をPrivateメソッドとして分離することで、コードの見通しが良くなり、内部処理の内容が明確になり、将来の拡張やテストも簡単になります。
よくあるミスと改善例
悪いケース | 改善案 |
---|---|
すべてのメソッドをPublicにする。 | 外部に必要なものだけをPublicにする。 |
メソッドが1つで処理が複雑。 | 処理ごとにPrivateメソッドへ分割する。 |
メソッド名が曖昧。(例:Data6) | 動詞から始まる名前にする。(例:LoadData) |
おわりに
クラスにおけるメソッドの設計は、単にコードの「動作」を書く以上に、「何を外部に公開し、何を隠すか」という設計思想が問われる重要なポイントです。
何を公開すべきか、を迷う場合は、クラスが保持する内部データをどうしたいのか、という点に着目すると、どのような公開メソッドがふさわしいか見えてきます。
Public と Private を意識した役割分担ができるようになると、コードの見通しや保守性が格段に向上します。少しずつでも意識して設計する習慣を身につけていきましょう。