ExcelVBAには標準モジュールとクラスモジュールが用意されているのですが、私が見たほとんどのVBAアプリでは標準モジュールのみが使用されています。
また、私が作成する時にも、標準モジュールのみで作成することが多いのですが大規模なアプリではクラスモジュールを使いたいと思う時もあります。
なぜ大規模なアプリでは、クラスモジュールを使ったほうが良いと思うのかを記載していこうと思います。
クラスモジュールを使うメリット
まずデメリットから書くのですが、クラスモジュールを使用すると大抵コードの記載量が多くなります。
またJavaのように継承などがないため、生産性が向上するかというと疑問です。(コンストラクタが使えるので、初期化を共通化出来るくらい)
なのでサクッと個人用VBAなどを作成したいときは、標準モジュールのみで作成したほうが良いかなと思っています。
複数人で開発するようなVBAや、長く使われることが想定されるVBAではクラスモジュールを使うことも悪くないと思います。理由は以下のメリットが有るためです。
- 共通関数を見つけやすくなる
- コード変更の影響を最小限に抑えられる
- Collectionで構造体は扱えないが、クラスは扱うことが出来る
1つずつ詳細に内容を記載してみたいと思います。
共通関数を見つけやすくなる
大規模なVBAを作成する場合には、同じような処理が出てこないように共通関数を作成することがほとんどだと思います。
例として、商品の価格(税抜)から消費税を取得するという関数を作成することを考えてみたいと思います。
↓クラスモジュールを使わない場合
Private Const TAX_RATE As Double = 0.1
Public Function getTax(ByVal price As Integer) As Integer
getTax = Round(price * TAX_RATE, 0)
End Function
呼び出す方法
Sub test()
MsgBox getTax(100)
End Sub
↓クラスモジュールを使う場合
'商品クラス
Private Const TAX_RATE As Double = 0.1
Private itemName
Private itemPrice
Public Sub setItemPrice(price As Integer)
itemPrice = price
End Sub
Public Function getTax() As Integer
getTax = Round(itemPrice * TAX_RATE, 0)
End Function
呼び出す方法
Sub test()
Dim it As item
Set it = New item
it.setItemPrice (100)
MsgBox it.getTax
End Sub
getTaxというという関数で消費税の額を返すようにしました。標準モジュールでもクラスモジュールでもほとんど同じ内容です。
大きく違うのが呼び出す方法です。
標準モジュールで組んだときには、getTaxという関数があるかどうかはプロジェクト内を検索しないとわかりません。
つまり複数人で開発をしていると見落としてしまう可能性があります。その場合、消費税計算用の関数が2つ作成されてしまうことも…。
一方でクラスの場合には、まずクラスを作成しなければいけません。そしてクラスを作成すると…

上記のように入力補完で「getTax」という関数がある事がわかるので、クラスを使うルールとしておけば共通関数を見つけられない事態は減っていくと思います。
VBAのコードは特にゴチャゴチャになりやすいので、処理が見つけやすくなることは大きなメリットに1つだと思います。
また、そのクラスに必要な動作がクラス内にまとまるので可読性も向上すると思います。
コード変更の影響を最小限に抑えられる
標準モジュールで共通関数を使用している場合は、共通関数の修正でデグレが発生するリスクが大きいです。
例えば、商品の価格(税込)を引数にして「¥100(税込み)」という形式の文字列を戻す関数があったとします。
↓クラスモジュールを使わない場合
Public Function getTaxStr(ByVal price As Integer) As String
getTaxStr = "¥" & CStr(price) & "(税込み)"
End Function
↓クラスモジュールを使う場合
'商品クラス
Private Const TAX_RATE As Double = 0.1
Private itemName As Integer
Private itemPrice As Integer
Private tax As Integer
Public Sub setItemPrice(price As Integer)
itemPrice = price
tax = Round(price * TAX_RATE, 0)
End Sub
Public Function getTaxStr() As String
getTaxStr = "¥" & CStr(tax) & "(税込み)"
End Function
getTaxStrという関数で「¥100(税込み)」という形式の文字列を返します。
ところが途中で顧客から要件の変更があり、「¥100(消費税込み)」という文言に変更しなければいけなくなったとします。
そこで、共通関数の「(税込み)」を「(消費税込み)」に変更しました。
その時に、別の開発者が事業主税の表示にgetTaxStr関数を使っていたらどうなるでしょうか?
事業主税の表示に「(消費税込み)」という文言が出てしまい、不具合となってしまいます。
クラスモジュールを使っていた場合には上記のような問題は発生しません。
何故ならgetTaxStr関数はItem(商品)クラスからのみ使用可能なため、他の税の計算には絶対使われないためです。
クラスを使わずに共通関数を多用してしまうと、どこが使用しているのかを調査した上でないと怖くて修正することができません。
つまりクラスを使用しないと、仕様変更による工数が大幅にアップしてしまいます。
1回しか使わないツールであればよいのですが、長く保守する必要があるVBAアプリの場合は、致命的な損害になりそうです。
Collectionで構造体は扱えないが、クラスは扱うことが出来る
クラスモジュールを使わずに、複数の値を格納できる型を使うときには構造体を使用します。
例えば商品の名前と価格を管理したい場合には以下の構造体を作成します。
'商品の構造体
Private Type Item
name As String
price As Integer
End Type
この構造体を使えば、クラスのフィールドの代替になりそうなのですが、何故か構造体はコレクションクラスで扱うことができません。
そのため、コレクションで複数の商品を扱いたいときにはクラスを使わざるを得なくなります。
コレクションは、変数をリストとして扱いたいときには、よく使用する機能なので、クラスを使わずに設計してしまい、後で困るということが発生するかもしれません。
まとめ
VBAのクラスモジュールは継承などがないのでメリットは薄めで、クラスでないと実装できない処理もありません。
またコードの記載も多くなりがちですが、保守性の面でかなりメリットがあると思います。
大規模なVBAアプリや長く使われるVBAアプリを作成する場合には、クラスを使用したほうが良いかと思います。
コメントを残す