ねらい:標準モジュールを「整理・分離・見える化」して保守を軽くする
VBAは増築しやすい反面、「同じ処理が散らばる」「責務が混ざる」「どこに何があるか分からない」になりがち。標準モジュールを意図的に整理すれば、修正範囲が明確になり、品質が安定します。初心者でもすぐ使えるテンプレと、重要ポイントを深掘りして解説します。
- 目的: 責務ごとにモジュールを分け、命名・可視性・共通ライブラリ化で壊れにくく
- 基本方針: 「役割別モジュール+命名規約+Public/Privateの使い分け+共通ヘッダ」
- 重要ポイント(深掘り):
- まず「責務の分離」。UI・業務ロジック・I/O・Config・共通基盤を分ける。
- Public/Privateで「外に見せる関数」を最小化。モジュール内の内部関数はPrivateに。
- 入口を揃え、開始・終了・例外・ログ・進捗の共通枠で包む。
モジュール構成テンプレ(役割別に揃える)
- ModApp(基盤): 開始・終了、最適化スイッチ、共通メッセージ
- ModConfig(設定): Configシート読み取りヘルパー
- ModIO(I/O): シート/ファイルの読み書き、最終行/列の取得
- ModLogic(業務ロジック): 計算・判定(純粋関数)
- ModService(ユースケース): IO×Logicの組み合わせ(業務処理の本体)
- ModUI(入口): ボタン/メニューからの起動、完了メッセージ
- ModProgress(進捗・停止): ステータスバー、プログレスバー、キャンセルフラグ
- ModLog(記録): Logシート出力、ステータス、ファイルログ(必要時)
重要ポイント(深掘り)
- 「分け方」が命: どこを見れば何があるか直感で分かる状態を作る。
- 1モジュール1責務: 2つ以上の責務が紛れているなら分割候補。
- 横断機能(App/Progress/Log)は共通化: すべての処理が同じ枠を使う前提。
モジュールの「骨組み」テンプレート(共通ヘッダ+可視性)
' ===== ModService: 社員取込ユースケース =====
' 役割: IOとLogicを組み合わせて「社員取込」を完了させる
' 公開: Run_EmployeeImport(入口)
' 依存: ModApp, ModIO, ModLogic, ModLog, ModProgress, ModConfig
Option Explicit
' 入口はPublic(UIや他モジュールから呼ばれる)
Public Sub Run_EmployeeImport()
On Error GoTo ErrHandler
AppEnter "社員取込 開始"
LogToSheet "Start", "社員取込"
' 設定(Configから)
Dim inputSheet As String: inputSheet = GetConfigString("INPUT_SHEET")
Dim outputSheet As String: outputSheet = GetConfigString("OUTPUT_SHEET")
' データ取得(IO)
Dim data As Variant: data = ReadInput(inputSheet)
' 業務ロジック(Logic)
Dim clean As Variant: clean = NormalizeEmployeeData(data)
Dim result As Variant: result = AddValidationFlag(clean)
' 出力(IO)
WriteOutput outputSheet, "A1", result
LogToSheet "Finish", "社員取込"
AppLeave True
MsgBox "社員取込が完了しました。"
Exit Sub
ErrHandler:
LogToSheet "Error", Err.Number & " - " & Err.Description
AppLeave True
MsgBox "失敗: " & Err.Description, vbExclamation
End Sub
' 内部ヘルパーはPrivate(外へ見せない)
Private Function NormalizeEmployeeData(ByVal data As Variant) As Variant
' ...(配列で整形)...
End Function
Private Function AddValidationFlag(ByVal data As Variant) As Variant
' ...(配列で判定)...
End Function
VB重要ポイント(深掘り)
- 共通ヘッダで「役割・公開・依存」を明記: 読んだ人が迷わない。
- Public/Privateの線引き: 入口はPublic、内部はPrivateで外部から触れさせない。
- Option Explicit徹底: 全モジュールに入れる。未宣言変数事故を根絶。
命名規約テンプレ(迷わない名前にする)
- モジュール名: ModApp/ModIO/ModLogic/ModService/ModUI…(頭にMod)
- 入口Sub: Run_XXXXX(例:Run_EmployeeImport)
- ヘルパー関数: 動詞+目的語(GetConfigString、ReadInput、WriteOutput)
- Private関数: 動詞+詳細(NormalizeEmployeeData、AddValidationFlag)
- 定数/列挙: 全大文字+アンダースコア(例:CONST PATH_EXPORT)、Enumで意味付け
' 定数と列挙(ModApp または専用ModConst)
Public Const DATE_FMT As String = "yyyy-mm-dd_HHNNSS"
Public Enum RunMode
ModeDev = 0
ModeProd = 1
End Enum
VB重要ポイント(深掘り)
- 「見て分かる」名前が最強: コメントより名前。
- 入口の命名が揃っていることが操作性を上げる: ボタン割当やメニューが楽。
使い回しライブラリを「1か所」に集める(重複排除)
よく使う汎用処理は共通化。重複があれば「共通モジュールへ移動」して参照側を書き換える。
' ModIO: よく使うI/O
Option Explicit
Public Function LastRow(ByVal ws As Worksheet, ByVal col As Variant) As Long
LastRow = ws.Cells(ws.Rows.Count, col).End(xlUp).Row
End Function
Public Function LastCol(ByVal ws As Worksheet, ByVal headerRow As Long) As Long
LastCol = ws.Cells(headerRow, ws.Columns.Count).End(xlToLeft).Column
End Function
Public Function ReadRegion(ByVal topLeft As Range) As Variant
ReadRegion = topLeft.CurrentRegion.Value
End Function
VB重要ポイント(深掘り)
- 同じコードを見つけたら統合: 「コピペ増殖」はバグの温床。
- 戻り型を揃える: Rangeではなく配列で返すとロジック分離がしやすい。
整理・棚卸しテンプレ(現状を可視化する)
既存プロジェクトの「どのモジュールに何があるか」を一覧化して、重複・命名違反・Option Explicit漏れを可視化。
' ModAudit: モジュール棚卸し
Option Explicit
Public Sub AuditModules()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet
On Error Resume Next
Set ws = wb.Worksheets("Audit")
If ws Is Nothing Then Set ws = wb.Worksheets.Add: ws.Name = "Audit"
On Error GoTo 0
ws.Cells.Clear
ws.Range("A1:D1").Value = Array("モジュール", "プロシージャ", "Public/Private", "Option Explicit")
Dim r As Long: r = 2
Dim vbComp As Object
For Each vbComp In wb.VBProject.VBComponents
If vbComp.Type = 1 Then ' 1=標準モジュール
Dim hasExplicit As Boolean
hasExplicit = InStr(1, vbComp.CodeModule.Lines(1, WorksheetFunction.Min(20, vbComp.CodeModule.CountOfLines)), "Option Explicit", vbTextCompare) > 0
Dim lineCount As Long: lineCount = vbComp.CodeModule.CountOfLines
Dim i As Long
For i = 1 To lineCount
Dim ln As String: ln = vbComp.CodeModule.Lines(i, 1)
If ln Like "Public Sub *" Or ln Like "Public Function *" Or ln Like "Private Sub *" Or ln Like "Private Function *" Then
ws.Cells(r, 1).Value = vbComp.Name
ws.Cells(r, 2).Value = Trim$(Split(Replace(ln, "(", " ("), " ")(2)) ' 名前をざっくり抽出
ws.Cells(r, 3).Value = IIf(InStr(1, ln, "Public", vbTextCompare) > 0, "Public", "Private")
ws.Cells(r, 4).Value = IIf(hasExplicit, "Yes", "No")
r = r + 1
End If
Next
End If
Next
ws.Columns.AutoFit
MsgBox "棚卸しを作成しました。(Auditシート)"
End Sub
VB重要ポイント(深掘り)
- 棚卸しで「整理の対象」を明確化: Public過多、Option Explicit漏れ、似た関数名を発見。
- 先に見える化→次にリファクタ: 「何がどこにあるか」を確信してから動く。
リファクタリング手順テンプレ(安全に整理する)
- 棚卸し(AuditModules)を作成。現状を把握。
- 命名を正す(入口Run_XXXXへ、モジュールは役割名へ)
- Publicを絞る(入口以外はPrivateへ、他モジュールからの直接呼び出しを禁止)
- 重複コードを共通化(ModIO/ModAppなどへ移動)
- 責務分離(UI/Service/Logic/IO/Configへ分割)
- Option Explicitを全モジュールへ(未宣言変数を潰す)
- テスト実行(主要処理が動くか。ログと進捗表示も確認)
重要ポイント(深掘り)
- 一度に全部やらない: 小さく切って、毎回動作確認。
- Public削減が最大効果: 外部参照を減らすほど壊れにくい構造になる。
依存の向きを揃える(逆流禁止ルール)
- UI → Service → Logic / IO / Config / App / Log / Progress
- 禁止例: LogicからRange操作(IOを通す)、ServiceからUserForm直接制御(UIへ戻す)
' 悪い例(Logicでセル操作)
Public Function CalcBad(ByVal ws As Worksheet) As Double
CalcBad = WorksheetFunction.Sum(ws.Range("A2:A100"))
End Function
' 良い例(IOで読み→Logicは計算)
Public Function CalcGood(ByVal arr As Variant) As Double
Dim i As Long, s As Double
For i = 1 To UBound(arr, 1)
s = s + Val(arr(i, 1))
Next
CalcGood = s
End Function
VB重要ポイント(深掘り)
- 依存の向きが揃うとテスト可能になる: Logicは配列だけで動く。
- 副作用の封じ込め: Range/Workbookへの書き込みはIOだけが担当。
モジュール輸出入(.basで管理)でバージョン管理に備える
- 手順: VBAエディタ→ファイル→ファイルのエクスポート(.bas)
- メリット: Gitなどで差分管理できる。整理の履歴が残る。
- 運用: 「共通ライブラリ」は別リポジトリにして他ブックへ流用
重要ポイント(深掘り)
- 1モジュール=1ファイル: 役割ごとに独立。
- 差分が見える: 命名やPublicの変更がレビューしやすい。
例題で練習(貼って試せる)
- 例1:棚卸し(AuditModules)を実行して、Public/PrivateとOption Explicit漏れを確認。
- 例2:命名整理(Run_XXXXX、ModXXXXX)へ揃える。
- 例3:重複統合(最終行取得やステータス更新をModIO/ModAppへ移動)。
- 例4:責務分離(Logicからセル操作を排除し、IO→Logic→IOの流れへ置換)。
- 例5:Public削減(入口のみPublic、他はPrivate)。動作確認。
実務の落とし穴と対策(ここが肝)
- 落とし穴1:整理で動かなくなる不安
- 対策: 小さく刻む+毎回テスト+ログ・進捗で見える化。
- 落とし穴2:Publicが多すぎて関係が複雑
- 対策: 入口だけPublic。他はPrivateに。Serviceからのみ呼ばせる。
- 落とし穴3:命名がバラバラで探せない
- 対策: モジュール先頭に共通ヘッダ(役割・公開・依存)を必ず入れる。
- 落とし穴4:Option Explicit漏れ
- 対策: 全モジュールへ挿入し、実行時に未宣言変数で止める。
- 落とし穴5:重複コードが消せない
- 対策: 共通モジュールを用意して「必ずそこを使う」ルールに。既存呼び出しを書き換える。
スターター手順(最短導入)
- AuditModulesで棚卸しを作り、現状を見える化。
- 命名・ヘッダ・Option Explicitを全モジュールに適用。
- Public整理+共通化(ModApp/ModIO/ModProgress/ModLog)。
- 責務分離(UI/Service/Logic/IO/Configへ移動)。
- .basエクスポートでバージョン管理に乗せ、変更履歴を残す。
