【Excel VBA】スパゲッティコードの終焉:20行の美学と「単一責任の原則」がもたらす堅牢な設計
こんにちは。開発プロジェクトの現場で数々の破綻したレガシーマクロを解体・再構築してきたチーフアーキテクトの私だ。
あなたの周りにもいないだろうか?
1つの `Sub` プロシージャの中に、シートのクリア、外部CSVの読み込み、複雑なIF分岐によるデータ加工、セルの着色、そしてメール送信までが延々と書き連ねられた、総行数300行超の「モンスタープロシージャ」が。
動けば正義、か? いや、それは幻想だ。
仕様変更のたびにバグが連鎖し、デバッグに何時間も溶かす。誰も全体を把握できず、最後には「触るな危険」の神棚に奉納される。
今回は、Excel VBAを「単なるスクリプトの貼り付け場」から、「保守性の高い堅牢なエンジニアリング」へと昇華させるための第一歩を伝授する。プロシージャの分割と「単一責任の原則(SRP)」だ。
—
なぜ「20行」なのか? プロシージャ肥大化の弊害
人間の脳のワーキングメモリには限界がある。1つの関数(プロシージャ)を目で追ったとき、スクロールせずに全体像を把握し、変数の状態を脳内でシミュレートできる限界値が、およそ「20行〜30行程度」なのだ。
これを超えたコードは、以下のような深刻な技術的負債を生む。
1. 認知的負荷の増大: どこで何をしているのか文脈を見失う。
2. 密結合の地獄: 変数がプロシージャ内でグローバルに使い回され、予期せぬ値の書き換え(副作用)が起きる。
3. テストの不可能性: 処理の一部だけを検証したくても、シート全体や外部ファイルが揃っていないと動かない。
これを解決するのが、「1つのプロシージャは1つの責任(仕事)だけを持つ」という単一責任の原則(Single Responsibility Principle)である。
—
【アンチパターン】すべてを飲み込む「神(God)プロシージャ」
まずは、現場でよく見かける「やってはいけない」典型例を見てみよう。
‘ 【アンチパターン】すべてを1人でこなす無能な神プロシージャ
Sub ProcessMonthlyData()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(“Data”)
‘ 1. データクリア
ws.Range(“A2:Z10000”).ClearContents
‘ 2. 外部CSVからのデータ取り込み(擬似コード)
Dim fileNum As Integer, lineData As String
fileNum = FreeFile
Open “C:\Data\monthly.csv” For Input As #fileNum
Dim r As Long: r = 2
Do While Not EOF(fileNum)
Line Input #fileNum, lineData
Dim fields() As String
fields = Split(lineData, “,”)
ws.Cells(r, 1).Value = fields(0)
ws.Cells(r, 2).Value = fields(1)
‘ (中略:ここで延々とデータクリーニングや計算を行う)
r = r + 1
Loop
Close #fileNum
‘ 3. フォーマット適用(色塗り、罫線)
Dim i As Long
For i = 2 To ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If ws.Cells(i, 2).Value > 10000 Then
ws.Cells(i, 2).Interior.Color = RGB(255, 200, 200)
End If
Next i
‘ 4. 完了ログの出力
MsgBox “処理が完了しました。”, vbInformation
End Sub
このコードの何が問題か?
「CSVのI/O」「データのパース」「ビジネスロジック(条件判定)」「UI(画面描画・メッセージ)」が1つの関数に密結合している。CSVの仕様が変わろうが、判定基準が変わろうが、この巨大な要塞全体を改修しなければならない。
—
【リファクタリング実践】責任を分離し、20行以内に収める
では、これをどう構造化すべきか。
処理を「データ取得」「ビジネスロジック」「出力・永続化」の各レイヤーに切り出し、司令塔となるメインプロシージャから呼び出す形にリファクタリングする。
以下が、プロダクションコードとして耐えうる堅牢な設計だ。
‘ ==========================================
‘ メインコントローラー(全体の流れを制御するだけ)
‘ ==========================================
Sub ExecuteMonthlyDataPipeline()
‘ エラーハンドリングの標準装備
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(“Data”)
‘ 各責任を持つプロシージャを呼び出す(それぞれ20行以内)
Call ClearExistingData(ws)
Call ImportCsvData(ws, “C:\Data\monthly.csv”)
Call ApplyBusinessRules(ws)
Application.ScreenUpdating = True
MsgBox “パイプライン処理が正常に完了しました。”, vbInformation
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
End Sub
‘ ==========================================
‘ 1. データクリアの責任
‘ ==========================================
Private Sub ClearExistingData(ByVal ws As Worksheet)
ws.Range(“A2:Z10000”).ClearContents
ws.Range(“A2:Z10000”).Interior.Color = xlNone
End Sub
‘ ==========================================
‘ 2. 外部I/O(CSV読み込み)の責任
‘ ==========================================
Private Sub ImportCsvData(ByVal ws As Worksheet, ByVal filePath As String)
Dim fileNum As Integer
fileNum = FreeFile
‘ ファイル存在チェック(実務で必須の堅牢性)
If Dir(filePath) = “” Then
Err.Raise vbObjectError + 1, “ImportCsvData”, “指定されたファイルが存在しません: ” & filePath
End If
Open filePath For Input As #fileNum
Dim currentRow As Long: currentRow = 2
Do While Not EOF(fileNum)
Dim lineData As String
Line Input #fileNum, lineData
Dim fields() As String
fields = Split(lineData, “,”)
ws.Cells(currentRow, 1).Value = fields(0)
ws.Cells(currentRow, 2).Value = fields(1)
currentRow = currentRow + 1
Loop
Close #fileNum
End Sub
‘ ==========================================
‘ 3. ビジネスロジック(データ加工・判定)の責任
‘ ==========================================
Private Sub ApplyBusinessRules(ByVal ws As Worksheet)
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then Exit Sub
Dim i As Long
For i = 2 To lastRow
' 1万円超のデータにアラート色を塗るロジック
If IsNumeric(ws.Cells(i, 2).Value) Then
If ws.Cells(i, 2).Value > 10000 Then
ws.Cells(i, 2).Interior.Color = RGB(255, 200, 200)
End If
End If
Next i
End Sub
—
この設計がもたらす圧倒的なアドバンテージ
上記のコードを見れば、プロシージャの肥大化を防ぎ、責務を分けることがなぜ強力なのかが理解できるはずだ。
1. 圧倒的な可読性: `ExecuteMonthlyDataPipeline` を見れば、このマクロが「クリアする → インポートする → ルールを適用する」という流れで動いていることが一目でわかる。
2. テストとデバッグの容易さ: 例えば「CSVの読み込み部分だけおかしい」と気づいた場合、`ImportCsvData` だけをイミディエイトウインドウ等で単体テスト・修正すればよく、他のロジックに影響を与えない。
3. スコープの限定(Privateの活用): 外部から呼び出される必要のない部品プロシージャには `Private` を付与し、モジュール外からの不要なアクセスを遮断。副作用を完全にコントロール下に入れる。
—
チーフアーキテクトからの提言
VBAを書くとき、私たちはつい「上から下に流し込むスクリプト」を書きがちだ。しかし、業務ツールが成長し、組織のインフラとして使われ続けるためには、「ソフトウェアエンジニアリングの原則」を適用しなければならない。
明日からコードを書くときは、こう自問してほしい。
- 「このプロシージャは、20行以内に収まっているか?」
- 「このメソッドは、1つ以外の余計な仕事をしていないか?」
この「20行の美学」を死守できたとき、あなたの書くVBAコードは見違えるほど堅牢になり、バグの恐怖から解放されるはずだ。さあ、今すぐあなたのレガシーモジュールを解体し、美しく構造化されたコードへとリファクタリングしたまえ。
