【入門編】【レガシー保守】スパゲッティコード化した巨大 VBScript を「Class 構文」で再構築するリファクタリング・ガイド – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

混沌から秩序へ:スパゲッティVBScriptを「Class構文」で解体・再構築する極意

「また、この数千行のスクリプトを修正しなきゃいけないのか……」

現場で、そんな溜息をついたことはありませんか?
上から下まで、どこでどの変数が書き換えられているのか分からない。修正した箇所が、全く関係ないはずの後半部分でエラーを引き起こす。変数名が `strData1`, `strData2` といった、意味を持たない名前で埋め尽くされている。

これらはすべて、「手続き型(プロシージャル)の限界」が生み出したスパゲッティコードの典型的な症状です。

こんにちは。今日は、そんなレガシーなVBScript(Windows Script Host)の荒波に立ち向かう皆さんのために、コードを「整理整頓」するだけでなく、「構造(アーキテクチャ)」から作り変える魔法をお伝えします。

その魔法の名は、「Class(クラス)構文によるオブジェクト指向への転換」です。

ここをクリアすれば、VBScriptの設計思想はバッチリですよ。さあ、一緒に新しい世界へ踏み出しましょう。

1. なぜ、あなたのコードは「スパゲッティ」になるのか?

リファクタリング(コードの改善)に入る前に、敵の正体を知っておきましょう。
なぜ、VBScriptはすぐにスパゲッティ化してしまうのでしょうか?

原因:グローバル変数の氾濫と「責務」の欠如

手続き型で書かれたコードでは、スクリプトの冒頭で宣言された変数が、プログラムのどこからでも読み書きできてしまいます。

  • 副作用の恐怖: `Sub ProcessData()` の中で、意図せず `strStatus` という変数を書き換えてしまい、その後の処理が狂う。
  • 依存関係の迷宮: 「この関数を直すと、あっちの処理が壊れる」という恐怖。これは、各パーツが独立しておらず、互いにベタベタと繋がっている(密結合)ことが原因です。

これを解決するのが、「カプセル化(Encapsulation)」という考え方です。

2. 「Class構文」という名の防壁を作る

Class構文を使うと、関連する「データ(変数)」と「処理(関数)」を一つの「オブジェクト(モノ)」としてパッケージ化できます。

例えば、「ログを出力する」という機能を作る場合、これまではあちこちに `WScript.Echo` やファイル書き込み処理を書いていたはずです。これを「Logger」というクラスに閉じ込めるのです。

イメージ図:手続き型 vs クラス型

【手続き型(スパゲッティ)】
`[変数A] → [処理1] → [変数B] → [処理2] → [変数Aを書き換え] …`
(すべてのデータが剥き出しで、誰でも触れる状態)

【クラス型(整理された構造)】
`[利用者] ───(命令)───> [Loggerクラス (中身はブラックボックス)]`
`[利用者] ───(命令)───> [FileProcessorクラス (中身はブラックボックス)]`
(データはクラスの中に隠され、外からは決められた「窓口」を通じてしか触れない)

3. 実践:スパゲッティからの脱却(Before & After)

それでは、具体的なコードを見てみましょう。
「ファイルの内容を読み込み、特定の文字が含まれていたらログに記録する」という処理を例にします。

【Before】典型的なスパゲッティコード

変数がバラバラに存在し、処理が混ざっています。

' --- スパゲッティコードの例 ---
Dim filePath, logPath, targetWord, content, file, fso
filePath = "C:\temp\data.txt"
logPath = "C:\temp\log.txt"
targetWord = "ERROR"

Set fso = CreateObject("Scripting.FileSystemObject")

' ファイル読み込み処理がメインロジックに混ざっている
If fso.FileExists(filePath) Then
    Set file = fso.OpenTextFile(filePath, 1)
    content = file.ReadAll
    file.Close
    
    ' 判定処理
    If InStr(content, targetWord) > 0 Then
        ' ログ出力処理が直接書かれている
        Dim logFile
        Set logFile = fso.OpenTextFile(logPath, 8, True)
        logFile.WriteLine "[" & Now & "] Found: " & targetWord
        logFile.Close
    End If
Else
    WScript.Echo "File not found."
End If

Set fso = Nothing

【After】Class構文による再構築

機能ごとにクラスを分離しました。これにより、メインの処理(ロジック)が驚くほど読みやすくなります。

' ============================================================
' リファクタリング後のコード
' ============================================================

' --- 1. Loggerクラス:ログ出力の責任を持つ ---
Class Logger
    Private logPath ' クラス内部の変数(外部からは直接触れない)

    ' 初期化(コンストラクタ的な役割)
    Public Sub Initialize(path)
        logPath = path
    End Sub

    ' ログを書き込むメソッド
    Public Sub WriteLog(message)
        Dim fso, logFile
        Set fso = CreateObject("Scripting.FileSystemObject")
        Set logFile = fso.OpenTextFile(logPath, 8, True) ' 8 = Append
        logFile.WriteLine "[" & Now & "] " & message
        logFile.Close
        Set fso = Nothing
    End Sub
End Class

' --- 2. FileAnalyzerクラス:ファイル解析の責任を持つ ---
Class FileAnalyzer
    Private fso
    
    Private Sub Class_Initialize()
        Set fso = CreateObject("Scripting.FileSystemObject")
    End Sub

    ' ファイル内に特定の単語があるか判定する
    Public Function ContainsWord(filePath, word)
        Dim file, content
        ContainsWord = False ' デフォルトはFalse
        
        If fso.FileExists(filePath) Then
            Set file = fso.OpenTextFile(filePath, 1)
            content = file.ReadAll
            file.Close
            
            If InStr(content, word) > 0 Then
                ContainsWord = True
            End If
        End If
    End Function
End Class

' ============================================================
' メインルーチン(ここが「司令塔」になる)
' ============================================================

Dim myLogger, myAnalyzer
Dim targetFile, targetWord, logFile

' 設定
targetFile = "C:\temp\data.txt"
targetWord = "ERROR"
logFile    = "C:\temp\log.txt"

' オブジェクトの生成と初期化
Set myLogger = New Logger
myLogger.Initialize(logFile)

Set myAnalyzer = New FileAnalyzer

' --- 劇的にスッキリしたメインロジック ---
If myAnalyzer.ContainsWord(targetFile, targetWord) Then
    myLogger.WriteLog "Target word '" & targetWord & "' was found in " & targetFile
Else
    ' 何もしない、あるいは別の処理
End If

' 後片付け
Set myLogger = Nothing
Set myAnalyzer = Nothing

WScript.Echo "処理が完了しました。"

4. 伝説のアーキテクトが教える「クラス設計の3つの鉄則」

クラスを使い始めたばかりの時に、陥りやすい罠があります。以下の3点を意識するだけで、あなたのコードの品質はプロ級になります。

① 「Property Get/Let/Set」を使いこなす

クラス内の変数(プロパティ)を外部に公開する場合、直接変数を見せるのではなく、「窓口」を通しましょう。

  • `Property Get`: 値を読み取るための窓口(読み取り専用にできる!)
  • `Property Let`: 数値や文字列などの値を書き込むための窓口
  • `Property Set`: オブジェクト自体を書き込むための窓口

これを使うことで、「変な値が代入されるのを防ぐ(バリデーション)」ことが可能になります。

② ひとつのクラスには「ひとつの責任」を

`FileAndLoggerAndEmailer`(ファイルもログもメールもやるクラス)のような、何でも屋のクラスを作ってはいけません。これは「神クラス」と呼ばれ、再びスパゲッティ化を招く元凶です。
「このクラスは何の専門家か?」を常に自問自答してください。

③ オブジェクトのライフサイクルを意識する

VBScriptはCOM(Component Object Model)の仕組みの上で動いています。
`Set obj = New ClassName` で作ったオブジェクトは、使い終わったら `Set obj = Nothing` で明示的に解放する癖をつけましょう。メモリリーク(メモリの無駄遣い)を防ぐ、プロの嗜みです。

5. まとめ:次の一歩へ

リファクタリングは、一度にすべてをやろうとすると挫折します。
まずは、「よく使う処理(例:ファイル操作、日付フォーマットなど)を一つ、クラスに切り出す」ことから始めてみてください。

小さな「秩序」が積み重なり、やがて巨大な「システム」へと進化していきます。

「コードが綺麗になると、仕事が楽しくなる」

これは、私たちがプロフェッショナルとして共有できる最高の感覚です。
もし道に迷ったら、いつでもこの記事に戻ってきてくださいね。

ここをクリアすれば、VBScriptの設計はもうあなたの手の中にあります。頑張って!

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