スコープの汚染を防ぐ:PrivateとPublicの厳格な使い分けルール
大規模なExcel VBA開発において、最も恐ろしいバグの正体を知っているか?
それは、どこからでもアクセスできる「グローバル変数(Public変数)」によるスコープの汚染だ。
「とりあえず動くから」と標準モジュールの頭に `Public` で変数を乱立させ、複数のマクロから値を書き換える。プロジェクトが肥大化したある日、その変数が意図せぬタイミングで書き換えられ、原因不明の無限ループやデータ破損を引き起こす――。開発現場で幾度となく見てきた、悪夢のような光景だ。
プログラミングの鉄則である「最小権限の原則」は、VBAにおいても絶対の真理である。今回は、モジュールと変数の公開範囲を厳格にコントロールし、修羅場をくぐり抜けてきたプロフェッショナルだけが知る「堅牢なカプセル化の設計思想」を伝授しよう。
—
1. なぜ「Public」の乱用はシステムを崩壊させるのか?
初心者が陥りがちな最大の罠が、「標準モジュールへの `Public` 変数の多用」だ。
‘ 【アンチパターン】すべてが見通せる「魔女のスープ」状態
Public g_TargetSheet As String
Public g_RowIndex As Long
Public g_DataCount As Long
これの何が問題か?
VBAの標準モジュールに定義された `Public` 変数は、アプリケーションが終了するまでメモリ上に常駐し、プロジェクト内のどこからでも読み書きが可能になる。
1. 依存関係の迷宮化: どのプロシージャがその変数を書き換えているのか、コードをくまなく追わなければならなくなる。
2. 並行処理・イベント競合の脆弱性: ワークシートのイベントやUserFormの操作などが複雑に絡み合った際、変数の値が意図せず上書きされる。
3. リセットリスク: VBAで「実行時エラー」が発生してデバッグモードに入ったとき、あるいは「リセットボタン(■)」を押したとき、Public変数の保持していたデータは一瞬で消え去るか、不正な状態のまま残存する。
結果として、「特定の順番でボタンを押さないと動かない」という、保守性ゼロの「動くスパゲッティコード」が完成する。
—
2. カプセル化の基本:Privateをデフォルトにする
堅牢なツールを構築するためのルールは極めてシンプルだ。
> 原則:すべての変数とプロシージャは `Private` から始めよ。外からアクセスが必要な場合のみ、限定的に `Public`(またはFriend)を許可せよ。
VBAにおけるアクセス修飾子の影響範囲を整理しておこう。
| 修飾子 | プロシージャ・変数のスコープ(見えている範囲) |
| :— | :— |
| Private (または省略) | 定義されたモジュール内のみで有効。他のモジュールからは隠蔽される。 |
| Public | プロジェクト全体のどこからでもアクセス可能。 |
変数は必ずプロシージャの内部(ローカル変数)で宣言し、プロシージャを跨いでデータを渡す必要がある場合は、引数(Arguments)を使う。これがデータフローを透明化する王道だ。
—
3. 【実践】保守性極限のプロダクションコード設計
ここでは、ファイル・データベース連携を想定した、実務でそのまま使える堅牢な設計パターンを示す。
「設定ファイルの読み込み」「データ処理」「ログ出力」という一連の処理を、標準モジュールとクラスモジュール(または適切なスコープ制御)を用いて美しくカプセル化する。
① 設定をカプセル化する(標準モジュール:`mConfig`)
外部のINIファイルや設定シートから読み込んだ値は、モジュールレベルの `Private` 変数に保持させ、外部からは「ゲッター(Getter)関数」を通じてのみ取得させる。直接変数を書き換えさせないのがポイントだ。
Option Explicit
‘ モジュール内のみで有効なPrivate変数(外部から直接書き換えられない)
Private m_ExportPath As String
Private m_IsDebugMode As Boolean
Private m_IsInitialized As Boolean
‘ 初期化処理(このモジュール内、または特定のトリガーからのみ呼ぶ)
Public Sub InitializeConfig()
If m_IsInitialized Then Exit Sub
‘ 実務ではここでINIファイルやワークシートから設定をロードする
m_ExportPath = ThisWorkbook.Path & “\output\”
m_IsDebugMode = True
m_IsInitialized = True
End Sub
‘ — ゲッタープロパティ(外部には「読む権利」だけを与える) —
Public Property Get ExportPath() As String
‘ 初期化されていなければ強制初期化
If Not m_IsInitialized Then InitializeConfig()
ExportPath = m_ExportPath
End Property
Public Property Get IsDebugMode() As Boolean
If Not m_IsInitialized Then InitializeConfig()
IsDebugMode = m_IsDebugMode
End Property
② ビジネスロジックの統括(標準モジュール:`mMainProcess`)
ユーザーが実行するエントリポイントとなるプロシージャ。ここでもスコープを意識し、外部から余計なプロシージャを呼ばせない。
Option Explicit
‘ ユーザーがリボンやボタンから実行するメイン処理
‘ 外部から直接叩かれるため Public
Public Sub RunDataProcessing()
‘ エラーハンドリングの標準装備
On Error GoTo ErrorHandler
‘ 処理開始のログ
Call mLogger.WriteLog(“処理を開始します。”)
‘ 設定の初期化(mConfigのカプセル化されたプロパティを利用)
mConfig.InitializeConfig
‘ 実務を想定したデータ処理の呼び出し(Privateプロシージャで隠蔽)
Call ExecuteCoreProcess(mConfig.ExportPath)
Call mLogger.WriteLog(“処理が正常に完了しました。”)
MsgBox “すべての処理が完了しました。”, vbInformation, “完了”
Exit Sub
ErrorHandler:
Call mLogger.WriteLog(“致命的エラー: ” & Err.Description)
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub
‘ このモジュール内だけで使うため Private
Private Sub ExecuteCoreProcess(ByVal targetPath As String)
‘ ディレクトリの存在確認とファイル出力のシミュレーション
If Dir(targetPath, vbDirectory) = “” Then
MkDir targetPath
End If
‘ ここに実際のデータ処理(CSV出力やDB連携など)を記述
Debug.WriteLine “出力先: ” & targetPath
End Sub
③ ログ出力を担当するモジュール(標準モジュール:`mLogger`)
ログ書き込みという単一責任を持たせたモジュール。これも内部のファイル番号管理などは完全に隠蔽する。
Option Explicit
‘ ログメッセージを書き込む(外部公開)
Public Sub WriteLog(ByVal message As String)
Dim fileNum As Integer
Dim logPath As String
logPath = ThisWorkbook.Path & “\app_error.log”
fileNum = FreeFile
‘ ログファイルへ追記
Open logPath For Append As #fileNum
Print #fileNum, “[” & Format(Now, “yyyy-mm-dd hh:nn:ss”) & “] ” & message
Close #fileNum
End Sub
—
4. この設計がもたらす圧倒的なメリット
上記のコード構造を導入することで、現場の開発において以下のメリットが爆発的に高まる。
1. 影響範囲の局所化(デバッグの容易化):
もしログの出力形式を変更したくなったら、修正するのは `mLogger` モジュール内だけだ。他のモジュールに影響が波及することは絶対にない。
2. データの安全弁(カプセル化):
`mConfig.ExportPath` は読み取り専用(`Property Get` のみ)として公開されているため、うっかり別の処理でパスの文字列を書き潰してしまうバグが物理的に不可能になる。
3. 高い可読性と分業体制:
「どのモジュールが何を責任持っているか」が明確なため、複数人での開発(ギット管理やファイル分割)においてもコンフリクトが起きにくくなる。
—
チーフアーキテクトからの提言
「VBAだから適当でいいや」という妥協は、数ヶ月後の自分、あるいは後任のエンジニアへの呪いとなる。
変数を公開するな。プロシージャを無駄にさらすな。
データを隠し、必要な窓口(インターフェース)だけを美しく整える。このカプセル化の思想を身につけた瞬間から、あなたの書くExcelマクロは「使い捨てのスクリプト」から、「堅牢な業務システム」へと進化する。
明日からのコードでは、まず宣言の頭に `Private` を打つクセを徹底してほしい。それだけで、あなたのコードの質は劇的に変わるはずだ。
