【入門編】プロシージャの分割と「単一責任の原則」:1つのメソッドを20行以内に収めるリファクタリングの第一歩 – Excel VBA解析バイブル

スポンサーリンク

こんにちは!VBAの基礎をマスターして、次のステージへ進もうとしているあなたへ。

これまで「マクロの記録」を使ったり、ネットで見つけたコードをコピペして何とか動かしたり……そんな時期を乗り越えて、「自分でゼロからコードを書いてみたい!」という熱意を持っている頃ではないでしょうか。

でも、実際に書き進めると、こんな壁にぶつかりませんか?

> 「気づいたら、1つのSubの中に100行も200行もコードが書かれてしまっている……」
> 「どこを直したら動くのか、自分でも分からなくなってきた……」
> 「変数があちこちで使い回されていて、頭がパンクしそう!」

これ、VBAを学び始めた人が全員通る「巨大スパゲッティコードの迷宮」です。

今日は、ここから抜け出し、プロのエンジニアも実践している「プロシージャの分割」と「単一責任の原則」についてお話しします。ここをクリアすれば、あなたの書くVBAコードは見違えるほど美しく、エラーに強くなりますよ。

1. なぜ「20行の壁」なのか?(マクロの記録からの脱却)

初心者が書きがちなコードは、上から下へ、すべての処理が1つのプロシージャにベタ書きされています。

  • シートを開いて
  • データをクリアして
  • ループを回して計算して
  • 条件分岐で色をつけて
  • メッセージを出して保存する

これらをすべて `Sub Main() … End Sub` の中に書くとどうなるでしょう?
コードの縦幅が長くなりすぎて、全体像が把握できなくなります。これを私たちは「見通しの悪い密林」と呼んでいます。

プロの現場では、「1つのプロシージャは20行以内(画面スクロールせずに全体が見える範囲)に収める」という不文律があります。

なぜなら、人間の脳が一度に理解できる情報の単位には限界があるからです。20行という制限を設けることで、自然とコードが整理され、メンテナンスしやすい構造に生まれ変わります。

2. 「単一責任の原則」とは何か?

プロシージャを分割するときの絶対的な指針が「単一責任の原則(Single Responsibility Principle)」です。

難しく聞こえますが、要するにこういうことです:

> 「1つのプロシージャは、仕事場(役割)を1つだけ持て」

例えば、レストランの厨房を想像してください。
シェフが1人で「食材の買い出し」「調理」「皿洗い」「接客」すべてを同時にやっていたら、お店はパニックになりますよね。だから、それぞれの担当(責任)に分割されています。

VBAのコードもまったく同じです。

  • 「データを取得する係」
  • 「計算する係」
  • 「シートに出力する係」

これらをきっちり分けること。これが単一責任の原則の本質なのです。

3. 【実例】スパゲッティコードを美しくリファクタリングする

百聞は一見に如かず。実際に「悪いコード」と「良いコード」を見比べてみましょう。

やりたいことはシンプルです。
「売上データシートからデータを読み込み、消費税を計算して、集計シートに書き出す」という処理です。

❌ 悪い例:すべてを詰め込んだ「モンスタープロシージャ」

Sub ProcessSalesData()
Dim wsRead As Worksheet
Dim wsWrite As Worksheet
Dim lastRow As Long
Dim i As Long
Dim subtotal As Double
Dim tax As Double

‘ 1. シートの準備
Set wsRead = ThisWorkbook.Sheets(“売上データ”)
Set wsWrite = ThisWorkbook.Sheets(“集計”)

‘ 2. 集計シートのクリア
wsWrite.Range(“A2:C1000”).Clear

‘ 3. 売上データを読み込んで計算して書き込み(ここに処理が集中!)
lastRow = wsRead.Cells(wsRead.Rows.Count, “A”).End(xlUp).Row

For i = 2 To lastRow
subtotal = wsRead.Cells(i, 2).Value
tax = subtotal 0.1

‘ 書き込み
wsWrite.Cells(i, 1).Value = wsRead.Cells(i, 1).Value ‘ ID
wsWrite.Cells(i, 2).Value = subtotal ‘ 小計
wsWrite.Cells(i, 3).Value = subtotal + tax ‘ 税込合計
Next i

‘ 4. 完了メッセージ
MsgBox “処理が完了しました!”, vbInformation
End Sub

動きますが……これ以上仕様変更(「やっぱり軽減税率8%の判定も入れて」など)があったら、どこを直せばいいか迷ってしまいますよね。

⭕ 良い例:役割ごとに分割された「チームワーク・コード」

それでは、このコードを「20行以内」&「単一責任の原則」に従って美しくリファクタリング(再構築)してみましょう。

‘ 【司令塔】全体の流れをコントロールするだけ(責任:全体の統括)
Sub MainProcessSales()
Dim wsRead As Worksheet, wsWrite As Worksheet

‘ シートの定義
Set wsRead = ThisWorkbook.Sheets(“売上データ”)
Set wsWrite = ThisWorkbook.Sheets(“集計”)

‘ 各専門プロシージャを呼び出す
ClearExistingData wsWrite
CalculateAndWriteData wsRead, wsWrite

MsgBox “処理が完了しました!”, vbInformation
End Sub

‘ 【専門家A】データをクリアする(責任:消去のみ)
Sub ClearExistingData(ByRef ws As Worksheet)
ws.Range(“A2:C1000”).Clear
End Sub

‘ 【専門家B】計算と書き込みを行う(責任:データ処理のみ)
Sub CalculateAndWriteData(ByRef wsRead As Worksheet, ByRef wsWrite As Worksheet)
Dim lastRow As Long
Dim i As Long
Dim subtotal As Double

lastRow = GetLastRow(wsRead, “A”)

For i = 2 To lastRow
subtotal = wsRead.Cells(i, 2).Value

‘ 書き込み処理をさらに細分化してもOKですが、今回はシンプルに
wsWrite.Cells(i, 1).Value = wsRead.Cells(i, 1).Value
wsWrite.Cells(i, 2).Value = subtotal
wsWrite.Cells(i, 3).Value = CalculateTaxIncluded(subtotal) ‘ 消費税計算を分離!
Next i
End Sub

‘ 【専門家C】税込み金額を計算する(責任:計算ロジックのみ)
Function CalculateTaxIncluded(ByVal subtotal As Double) As Double
‘ 10%の税込み計算をカプセル化(税率が変わってもここを変えるだけ!)
CalculateTaxIncluded = subtotal 1.1
End Function

‘ 【便利屋】最終行を取得する(責任:位置特定のみ)
Function GetLastRow(ByRef ws As Worksheet, ByRef colStr As String) As Long
GetLastRow = ws.Cells(ws.Rows.Count, colStr).End(xlUp).Row
End Function

どうでしょう?
メインの処理である `MainProcessSales` を見れば、「シートを準備して、クリアして、計算して、終わりのメッセージを出す」という全体像が、コードを読んだ瞬間に頭に入ってきますよね。

それぞれの細かい仕事は、下にある短いプロシージャ(サブルーチンやファンクション)に綺麗に任せられています。

4. プロシージャ分割の3大メリット

このようにコードをパーツに分けることで、あなたの開発ライフは劇的に変わります。

1. 「バグの犯人」がすぐ見つかる
計算結果がおかしいなら `CalculateTaxIncluded` や `CalculateAndWriteData` だけを見ればよく、シートの消去処理を疑う必要がなくなります。
2. 部品の「使い回し(再利用)」ができる
`GetLastRow` なんて、他のマクロでも100回使えますよね。一度書けば、コピペすらいらずに呼び出せます。
3. 仕様変更に圧倒的に強い
「消費税率が10%から変わるかも」と言われた時、良い例なら `CalculateTaxIncluded` の ` 1.1` を ` 1.08` に書き換える(あるいは新税率に対応させる)だけで終わりです。他のコードに影響を与えません。

5. 初心者が陥りやすい罠:変数のスコープ(通用範囲)

プロシージャを分けるとき、一つだけ注意すべきルールがあります。それが「変数のスコープ」です。

例えば、`Sub A` の中で宣言した変数は、`Sub B` の中では使うことができません(それぞれ別の部屋にいるようなものです)。

データを別のプロシージャに渡したいときは、「引数(ひきすう)」という仕組みを使います。

‘ 呼び出し元
Sub ParentProc()
Dim myName As String
myName = “VBAエンジニア”

‘ 子プロシージャに「myName」の値を渡す
ChildProc myName
End Sub

‘ 呼び出し先(受け取る側)
Sub ChildProc(ByVal nameVal As String)
MsgBox “こんにちは、” & nameVal & “さん!”
End Sub

「引数って難しそう……」と感じるかもしれませんが、「他の部屋に荷物をパスするパイプ役」だと思えば簡単です。最初は `ByRef`(参照渡し)や `ByVal`(値渡し)の厳密な違いで悩むよりも、「データをバトンタッチしているんだな」という感覚を掴むことが大切です。

まとめ:今日から実践できる一歩

いかがでしたか?
プロシージャの分割と「単一責任の原則」は、プログラミングを「苦行」から「パズルを解くような楽しさ」に変えてくれる魔法のスキルです。

  • 1つのプロシージャは20行以内を意識する
  • 「何をする係か」を1つに決める
  • 長いコードを書きたくなったら、パーツに切り出す

ここをクリアすれば、あなたはもう「マクロの記録を貼り付けるだけの人」ではありません。立派なVBAアプリケーション・デザイナーです。

ぜひ、今手元にあるあなたのコードを開いて、「あ、ここ長すぎだな」と思うところをパーツに切り分けてみてください。その瞬間から、世界が変わって見えるはずですよ。それでは、開発の現場でお会いしましょう!

タイトルとURLをコピーしました