混沌から秩序へ:スパゲッティ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の設計はもうあなたの手の中にあります。頑張って!
