標準モジュールに処理を書きすぎて、気づけば1つのファイルが3000行を超えていた——そんな反省から、ThisWorkbookとクラスモジュールへの責務分割を見直すことになりました。役割ごとに置き場所を決めておくと、半年後の自分が読み返すときの負担がまるで違います。
モジュール種別の一覧
図1: VBAモジュール種別の役割分担
| 種別 | VBEでの表示 | 主な用途 |
|---|---|---|
| 標準モジュール | Module1 など | 汎用プロシージャ・ユーティリティ関数 |
| ThisWorkbook | ThisWorkbook | ブック全体のイベント(Open・BeforeClose等) |
| シートモジュール | Sheet1 など | シート固有のイベント(Change・SelectionChange等) |
| クラスモジュール | Class1 など | オブジェクト指向的な設計・カプセル化 |
| UserForm | UserForm1 など | ダイアログUI・入力フォーム |
標準モジュール
最も一般的なモジュールです。Public Sub / Public Function で宣言するとプロジェクト全体から呼び出せます。
Option Explicit
' ── modUtils.bas ──────────────────────────────
' 役割:汎用ユーティリティ関数を集約する
' ファイルが存在するか確認
Public Function FileExists(ByVal path As String) As Boolean
FileExists = (Dir(path) <> "")
End Function
' 文字列をゼロパディング
Public Function ZeroPad(ByVal num As Long, ByVal digits As Integer) As String
ZeroPad = Right(String(digits, "0") & CStr(num), digits)
End Function
💡 命名規則の推奨
標準モジュールには mod プレフィックスをつけて役割を明確にするのが一般的です(例: modUtils・modFileIO・modConfig)。
ThisWorkbook モジュール
ブック全体に関わるイベントを記述します。Workbook_Open や Workbook_BeforeClose など、自動実行が必要な処理の起点になります。
Option Explicit
' ── ThisWorkbook ──────────────────────────────
' ブックを開いたとき
Private Sub Workbook_Open()
modSetup.InitializeApp ' 初期化処理を標準モジュールに委譲
End Sub
' ブックを閉じる前
Private Sub Workbook_BeforeClose(Cancel As Boolean)
modSetup.CleanupApp ' クリーンアップを標準モジュールに委譲
End Sub
✅ ThisWorkbook は薄く保つ
イベントの「入口」として機能させ、実際の処理は標準モジュールに委譲するのがベストプラクティスです。ThisWorkbook にロジックを詰め込むと他からテストしにくくなります。
シートモジュール
各シートオブジェクトに紐づくモジュールです。シート固有のイベントを実装します。
Option Explicit
' ── Sheet1(データ入力シート)────────────────
' セル変更時にバリデーション
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 3 Then ' C列が変更された場合
If Not IsNumeric(Target.Value) Then
MsgBox "数値を入力してください", vbExclamation
Application.Undo
End If
End If
End Sub
クラスモジュール(概要)
オブジェクト指向的な設計を実現します。詳細は PART 07 で解説しますが、ここでは概念だけ押さえておきましょう。
' ── clsProduct.cls ──────────────────────────
Option Explicit
Private m_Name As String
Private m_Price As Long
Public Property Get Name() As String: Name = m_Name: End Property
Public Property Let Name(v As String): m_Name = v: End Property
Public Property Get Price() As Long: Price = m_Price: End Property
Public Property Let Price(v As Long): m_Price = v: End Property
' ── 呼び出し側 ──
Sub UseProduct()
Dim p As New clsProduct
p.Name = "テスト商品"
p.Price = 1000
MsgBox p.Name & " : ¥" & p.Price
End Sub
UserForm モジュール
UIとロジックを分離するのが重要です。フォームモジュールにはUI操作だけを記述し、ビジネスロジックは標準モジュールへ委譲します。
Option Explicit
' ── frmInput ──────────────────────────────────
Private Sub btnOK_Click()
' フォームはデータ収集のみ、処理は標準モジュールに委譲
Dim inputVal As String
inputVal = Me.txtValue.Text
If inputVal = "" Then
MsgBox "入力してください", vbExclamation
Exit Sub
End If
modProcess.ExecuteWithInput inputVal
Me.Hide
End Sub
Private Sub btnCancel_Click()
Me.Hide
End Sub
責務分担の設計指針
| 何を書くか | どこに書くか |
|---|---|
| 汎用関数・共通処理 | 標準モジュール(modUtils など) |
| 定数・設定値 | 標準モジュール(modConst など) |
| ブックの起動・終了処理 | ThisWorkbook(処理本体は標準モジュールへ委譲) |
| シート固有の入力チェック | シートモジュール |
| データ構造・ビジネスオブジェクト | クラスモジュール |
| UI操作 | UserForm(ロジックは標準モジュールへ委譲) |
まとめ
モジュールを適切に分割すると、「どこに何が書いてあるか」が自然に分かり、デバッグや機能追加のコストが大幅に下がります。特に ThisWorkbook や UserForm は薄く保ち、ロジックは標準モジュールへ という原則が重要です。
✅ 次の章では…
PART 04 では CreateObjectによる別プロセス起動 を解説します。WorkBook.Open 時の画面ちらつきを根本解決する方法を紹介します。
VBAモジュール設計でよくある誤り
モジュール分割を誤るとコードが肥大化し、保守や再利用が困難になります。設計段階でよくある誤りを確認しておきましょう。
| 誤り/失敗パターン | 何が起きるか | 正しい対処/防止策 |
|---|---|---|
| 全処理をThisWorkbookや1つの標準モジュールに書く | コードが数千行に膨れ上がり、どこに何があるか分からなくなる | 機能・目的ごとにモジュールを分割し、ThisWorkbookはイベント処理のみ、ロジックは標準モジュールに分ける |
| グローバル変数を多用して状態管理する | 変数の変更箇所が特定できず、意図しない値変更でバグが混入しやすくなる | グローバル変数は最小限に留め、モジュール内変数(Private)や引数・戻り値でデータを受け渡す |
| 同じ処理をコピー&ペーストで複数箇所に書く | 修正が必要になった際に全箇所を修正し忘れ、バグが残る | 共通処理はサブプロシージャや関数として切り出し、呼び出し側からCall/参照するように設計する |