Excel VBAの標準モジュールとクラスモジュールの違いを、ポイントに絞って備忘録します。あくまで私個人の判断基準です。ご参考になれば幸いです。
根本的な違い
標準モジュール | クラスモジュール | |
---|---|---|
性質 | 静的(1つだけ存在)※ | 動的(複数作成可能) |
状態 | グローバル共有 | インスタンス毎に独立 |
用途 | 共通処理・ユーティリティ | データ + 処理のセット |
初期化 | なし | Class_Initialize/Terminate |
イベント | なし | WithEvents対応 |
※モジュール自体が静的という意味(Static変数とは別概念)
具体例で理解する
標準モジュール:共通のツール箱
' UtilityModule.bas
Option Explicit
' Option Private Module ' 他のプロジェクトからの参照を制限
' アプリケーション全体で共有される
Public g_ProcessCount As Long
' 誰でも使える道具(関数)
Public Function FormatMoney(value As Double) As String
FormatMoney = "¥" & Format(value, "#,##0")
End Function
Public Sub WriteLog(message As String)
' ログファイルに出力(共通処理の例)
Debug.Print Now & ": " & message
End Sub
Public Sub IncrementCount()
g_ProcessCount = g_ProcessCount + 1
WriteLog "処理回数: " & g_ProcessCount
End Sub
使い方: 直接呼び出し
Debug.Print FormatMoney(1000) ' ¥1,000
IncrementCount() ' グローバルカウンターが増える
クラスモジュール:独立したオブジェクト
' BankAccount.cls
Option Explicit
Private m_Balance As Double
Private m_AccountNumber As String
' 初期化処理
Private Sub Class_Initialize()
m_Balance = 0
Debug.Print "口座オブジェクト作成"
End Sub
' 終了処理(リソース解放)
Private Sub Class_Terminate()
Debug.Print "口座オブジェクト(" & m_AccountNumber & ")削除"
End Sub
Public Property Let AccountNumber(value As String)
m_AccountNumber = value
End Property
Public Property Get Balance() As Double
Balance = m_Balance
End Property
Public Sub Deposit(amount As Double)
m_Balance = m_Balance + amount
End Sub
Public Sub Withdraw(amount As Double)
If amount <= m_Balance Then
m_Balance = m_Balance - amount
End If
End Sub
' イベント発生例
Public Event BalanceChanged(NewBalance As Double)
Private Sub NotifyBalanceChange()
RaiseEvent BalanceChanged(m_Balance)
End Sub
使い方: インスタンス作成して使用
Dim account1 As New BankAccount
Dim account2 As New BankAccount
account1.AccountNumber = "001"
account1.Deposit 1000
account2.AccountNumber = "002"
account2.Deposit 2000
' それぞれ独立した残高を持つ
Debug.Print account1.Balance ' 1000
Debug.Print account2.Balance ' 2000
' Collection で複数管理も可能
Dim accounts As New Collection
accounts.Add account1, "001"
accounts.Add account2, "002"
使い分けの判断
標準モジュールを使う場面
' ✅ 状態を持たない処理
Public Function CalculateTax(price As Double) As Double
CalculateTax = price * 0.1
End Function
' ✅ アプリケーション全体の設定・定数
Public Const APP_VERSION As String = "1.0"
Public g_LastSavedFile As String
' ✅ エラーハンドリング
Public Sub HandleError(errNum As Long, errDesc As String)
WriteLog "エラー " & errNum & ": " & errDesc
End Sub
クラスモジュールを使う場面
' ✅ データと処理がセット
' 顧客情報 + 顧客の操作
' 注文情報 + 注文の処理
' ファイル情報 + ファイル操作
' ✅ 同じ種類のものを複数扱う
' 複数の顧客、複数の注文、複数のファイル
' ✅ イベント駆動が必要
' WithEvents を使った通知パターン
実践例:同じ処理での比較
データ処理を例に、実装の違いを見てみましょう。
標準モジュール版
' DataProcessor.bas
Public g_ProcessedCount As Long ' 全体で共有
Public Sub ProcessData(data As String)
' 処理実行
g_ProcessedCount = g_ProcessedCount + 1
Debug.Print "処理済み件数: " & g_ProcessedCount
End Sub
問題点:
- 複数の処理を同時に行うと、カウンターが混在する
- 前の処理の影響を受ける
クラスモジュール版
' DataProcessor.cls
Private m_ProcessedCount As Long ' インスタンス毎に独立
Public Property Get ProcessedCount() As Long
ProcessedCount = m_ProcessedCount
End Property
Public Sub ProcessData(data As String)
' 処理実行
m_ProcessedCount = m_ProcessedCount + 1
Debug.Print "処理済み件数: " & m_ProcessedCount
End Sub
利点:
Dim processor1 As New DataProcessor
Dim processor2 As New DataProcessor
processor1.ProcessData "データA" ' processor1のカウント: 1
processor2.ProcessData "データB" ' processor2のカウント: 1
' それぞれ独立してカウント
Debug.Print processor1.ProcessedCount ' 1
Debug.Print processor2.ProcessedCount ' 1
まとめ
選択の指針(判断フローチャート)
データや状態を保持する必要がある?
├─ No → 標準モジュール(ユーティリティ関数)
└─ Yes → 複数のインスタンスが必要?
├─ No → 標準モジュール(グローバル変数)
└─ Yes → クラスモジュール
パフォーマンス面の注意点
標準モジュール
- ✅ 呼び出しコストが低い(1回ロードで使い回し)
- ❌ グローバル変数の乱用でメモリリーク的バグが発生しやすい
クラスモジュール
- ✅ 独立した状態で安全
- ❌ インスタンス作成にわずかなコストあり
覚えておくべき1つのルール
「複数作る可能性があるか?」
- No → 標準モジュール
- Yes → クラスモジュール
あくまで私個人の判断基準です。ご参考になれば幸いです。
参考