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

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

スパゲッティの深淵から脱却せよ:VBScriptを「Class構文」で再構築するアーキテクチャ設計術

業務自動化の現場において、我々は避けて通れない「怪物」に直面することがある。

数千行に及ぶ、延々と続く `If…Then…Else` の迷宮。どこからともなく湧いてくるグローバル変数の汚染。一箇所を修正すれば、全く関係のない箇所でエラーを吐き出す、予測不能な副作用。そう、「手続き型で書き殴られた、巨大なスパゲッティ・スクリプト」だ。

多くの開発者は、これを「VBScriptの限界」だと諦める。しかし、それは大きな間違いだ。VBScriptには `Class` 構文が存在する。このオブジェクト指向の概念を正しく導入できるか否かが、単なる「スクリプト作成者」と、信頼される「システムアーキテクト」を分かつ境界線となる。

本稿では、レガシーなVBScriptを、保守可能で堅牢な「クラスベース・アーキテクチャ」へと昇華させるための極限の知見を伝授する。

1. なぜ、あなたのコードは「壊れやすい」のか

リファクタリングを始める前に、敵の正体を理解せよ。スパゲッティコードがなぜ致命的なのか。理由は主に3つある。

グローバル変数の氾濫(状態の不透明性)

プロシージャ間で共有される変数があちこちで書き換えられる設計は、プログラムの「状態」を予測不可能にする。どの関数がいつ変数を変えたのか、追跡することはもはや不可能だ。

責務の混濁(Single Responsibility Principleの欠如)

「ファイルを読む」「計算する」「ログに書く」「画面に出す」……これらが一つの巨大な `Sub` や `Function` に詰め込まれている。これでは、ファイル形式が変わっただけで、計算ロジックまで修正しなければならなくなる。

エラーハンドリングの不在

`On Error Resume Next` を魔法の杖のように使い、エラーを握りつぶす。これは「バグを隠蔽している」だけであり、本番環境での爆弾を製造しているに等しい。

2. クラスによる「境界線」の構築

リファクタリングの核心は、「データ(状態)」と「振る舞い(ロジック)」を一つのユニット(クラス)に閉じ込めることにある。

VBScriptの `Class` を使う目的は、単にコードを整理することではない。「外部から干渉できない、独立した部品」を作ることにある。

設計の黄金律

1. カプセル化: プロパティは可能な限り `Private` にし、変更は専用の `Method` を通じて行う。
2. 依存性の注入 (Dependency Injection): クラス内で直接 `FileSystemObject` を生成するのではなく、外部から渡す、あるいはコンストラクタで初期化する設計を意識せよ。これにより、テストが容易になる。
3. 単一責任: 「ログ出力クラス」はログを書くことだけに集中させる。「CSV解析クラス」は解析だけに集中させる。

3. 実戦:堅牢なプロダクションコード・パターン

では、具体的な実装例を示そう。
「CSVファイルを読み込み、特定の条件でデータを抽出し、結果をログに出力する」という業務シナリオを想定する。

以下のコードは、単なるサンプルではない。「エラーハンドリング」「オブジェクトのライフサイクル管理」「責務の分離」を組み込んだ、実戦投入可能なアーキテクチャだ。

' ==========================================================================
' Project: Enterprise Automation Refactoring Guide
' Description: Class-based architecture for robust VBScript execution
' ==========================================================================
Option Explicit

' --- Main Execution Flow ---
' プログラムの入り口。ロジックを直接書かず、コントローラーを呼び出す。
Sub Main()
    Dim logger, processor, csvPath, targetValue
    
    csvPath = "C:\temp\data.csv"
    targetValue = "Active"

    ' 1. インスタンス化 (Dependency Injectionの準備)
    Set logger = New Logger
    Set processor = New DataProcessor
    
    ' ログの初期化
    logger.Initialize "C:\temp\process.log"

    On Error Resume Next ' 実行レベルのエラーハンドリングを開始

    logger.Info "--- Processing Started ---"

    ' 2. 業務ロジックの実行
    ' processorにloggerを渡すことで、processorはログ出力機能を利用できる
    Set processor.Logger = logger
    
    If processor.ProcessFile(csvPath, targetValue) Then
        logger.Info "Process completed successfully."
    Else
        logger.Error "Process failed. Check the source file or format."
    End If

    If Err.Number <> 0 Then
        logger.Error "Critical System Error: " & Err.Description
    End If

    logger.Info "--- Processing Ended ---"

    ' 3. クリーンアップ (オブジェクトのライフサイクル管理)
    On Error GoTo 0
    Set processor = Nothing
    Set logger = Nothing
End Sub

' ==========================================================================
' CLASS: Logger
' 責務: ファイルへのログ記録
' ==========================================================================
Class Logger
    Private fso
    Private logFile
    Private logPath

    ' コンストラクタ的な初期化メソッド
    Public Sub Initialize(path)
        Set fso = CreateObject("Scripting.FileSystemObject")
        logPath = path
        ' 追記モードでファイルを開く
        Set logFile = fso.OpenTextFile(logPath, 8, True)
    End Sub

    Public Sub Info(msg)
        WriteLog "INFO", msg
    End Sub

    Public Sub Error(msg)
        WriteLog "ERROR", msg
    End Sub

    ' Privateメソッドにより、外部からの不適切な書き込みを防止
    Private Sub WriteLog(level, msg)
        If Not logFile Is Nothing Then
            logFile.WriteLine "[" & Now & "] [" & level & "] " & msg
        End If
    End Sub

    ' デストラクタに相当するクリーンアップ
    Public Sub Close()
        If Not logFile Is Nothing Then
            logFile.Close
            Set logFile = Nothing
        End If
        Set fso = Nothing
    End Sub
End Class

' ==========================================================================
' CLASS: DataProcessor
' 責務: CSVの解析とビジネスロジックの実行
' ==========================================================================
Class DataProcessor
    ' 外部クラスへの参照(依存性)
    Public Logger 
    Private fso

    Private Sub Class_Initialize()
        Set fso = CreateObject("Scripting.FileSystemObject")
    End Sub

    ' メインのロジック
    Public Function ProcessFile(filePath, filterValue)
        Dim file, line, parts
        ProcessFile = False ' デフォルトは失敗

        ' ファイルの存在確認
        If Not fso.FileExists(filePath) Then
            If Not Logger Is Nothing Then Logger.Error "File not found: " & filePath
            Exit Function
        End If

        Set file = fso.OpenTextFile(filePath, 1)
        
        Do Until file.AtEndOfStream
            line = file.ReadLine
            If line <> "" Then
                ' 簡易的なCSV分割(実務ではカンマ区切りを考慮)
                parts = Split(line, ",")
                
                ' フィルタリングロジック
                If UBound(parts) >= 1 Then
                    If parts(1) = filterValue Then
                        If Not Logger Is Nothing Then Logger.Info "Match Found: " & parts(0)
                        ' ここに実際のデータ処理(DB登録など)を記述する
                    End If
                End If
            End If
        Loop

        file.Close
        Set file = Nothing
        ProcessFile = True ' すべて正常に終了した場合のみTrueを返す
    End Function

    Private Sub Class_Terminate()
        Set fso = Nothing
    End Sub
End Class

' プログラム実行
Main

4. プロフェッショナルが守るべき「細部」の規律

コードを書く際、以下の3点に魂を込めろ。これこそが、保守性の差を生む。

I/Oリソースの厳格な管理

`FileSystemObject` や `ADODB.Connection` などのリソースは、使い終わったら即座に `Set obj = Nothing` し、ファイルなら `.Close` を呼び出せ。VBScriptのガベージコレクションを過信するな。リソースの解放漏れは、長時間稼働する自動化ツールにおいて「メモリリーク」と「ファイルロック」という名の死を招く。

エラーハンドリングの階層化

`On Error Resume Next` は、「エラーが予想される最小単位のスコープ」でのみ使用せよ。
クラス全体を `On Error Resume Next` で覆うのは愚策だ。エラーが発生した場所を特定できなくなる。エラーが発生したら、そのクラス内で適切に処理(ログ出力)を行い、呼び出し元に「失敗した事実」を戻り値(Boolean等)で伝えるのがプロの設計だ。

依存関係の可視化

上記の例では、`DataProcessor` が `Logger` をプロパティとして持っている。これは「`DataProcessor` はログ機能に依存している」ことを明示している。このように、クラス間の関係性を明確にすることで、将来的に「ログをファイルではなく、データベースに書き換えたい」となった際、`Logger` クラスだけを差し替えれば済むようになる。

結びに代えて

VBScriptは、決して「使い捨てのスクリプト言語」ではない。
適切な設計思想を持ち、`Class` を駆使して構造化すれば、企業の基幹業務を支える極めて強力な武器へと進化する。

「動けばいい」という考えを捨てろ。
「次にこのコードを読む人間が、迷わず、安心して修正できるか?」
この問いに答えられる設計こそが、真に価値のある自動化エンジニアリングである。

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