こんにちは!エンジニアリングの世界へようこそ。
今日は、VB.NETでの開発において避けては通れない、けれど誰もが一度は沼にハマるテーマについてお話しします。
「あちこちに処理を書き散らすのは面倒だから、とりあえず`Module`(モジュール)に何でもかんでも突っ込んでおけ!」
――古いVBA(Visual Basic for Applications)や、初期のVBのノリのままコードを書いていると、ついやってしまいがちですよね。どこからでも呼び出せて本当に便利です。
しかし、その「便利さ」の裏側で、あなたの書いたコードは「魔改造不可能なスパゲッティコード」への道を歩み始めているかもしれません。
今回は、VB.NETの`Module`の光と闇を紐解き、グローバル関数の誘惑に打ち勝って「保守性の高いオブジェクト指向の設計」へと脱皮するための極意を伝授します。ここをクリアすれば、あなたのVB.NETのスキルは間違いなくプロの領域に到達しますよ!
—
1. モジュール(Module)とは何か? なぜ使いたくなるのか
まずは敵を知ることから始めましょう。VB.NETにおける`Module`とは、インスタンス化(`New`すること)なしに、どこからでも中の関数や変数にアクセスできる「入れ物」です。
魅惑の「どこからでも呼べる」世界
例えば、日付のフォーマット変換や、ちょっとしたログ出力など、アプリのどこからでも使いたい共通処理があるとします。
‘ 【悪魔のささやきコード】Moduleによるグローバル関数の定義
Module CommonUtils
‘ どこからでも呼ばれるグローバル変数
Public AppVersion As String = “1.0.0”
‘ インスタンス化不要で呼び出せる関数
Public Sub ShowMessage(ByVal message As String)
MsgBox(“[” & AppVersion & “] ” & message)
End Sub
End Module
これの何が便利かというと、呼び出す側で以下のように書ける点です。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
‘ Newしなくても、そのまま呼べる!
CommonUtils.ShowMessage(“処理が完了しました”)
End Sub
End Class
「`New`の手間がいらないなんて、最高じゃないか!」と思いますよね。
しかし、この手軽さこそが、プロジェクトを崩壊させるトロイの木馬なのです。
—
2. モジュール(Module)の「罪」:なぜ保守性を殺すのか
エンジニアとしての経験を積めば積むほど、私たちは`Module`を避けるようになります。それには明確な理由があります。
① 「状態」がグローバルに共有され、バグの温床になる
Module内の変数(`Public`変数など)は、メモリ上にたった一つしか存在しません。アプリのどこからでも書き換え可能です。
「A画面で書き換えた変数の値を、B画面で参照したら勝手に変わっていた……」なんていう、原因究明が極めて困難なバグ(副作用)の温床になります。
② 単体テスト(Unit Test)が不可能になる
モダンな開発では、プログラムの部品ごとに正しく動くかテスト(自動テスト)を書きます。しかし、Module内の関数が「外部のグローバル変数」や「UI(画面の部品)」に依存していると、テスト環境で独立して動かすことができなくなります。
「テストできないコード=変更するたびに人間が目視で確認するしかない爆弾コード」です。
③ オブジェクト指向の恩恵をすべて捨てることになる
VB.NETは強力なオブジェクト指向言語です。しかし、Moduleばかり使っていると、それは実質的に「古いProcedural(手続き型)言語」を書いているのと変わりません。.NETが持つカプセル化やポリモーフィズムといった素晴らしい機能を、自ら封印していることになります。
—
3. 実践!Moduleからクラス設計へのリファクタリング
では、具体的にどう直せばよいのでしょうか?
「共通関数をまとめる」という目的はそのままに、オブジェクト指向の「クラス(Class)」を使った設計へ移行してみましょう。
改善前:Moduleによる手続き的な実装
先ほどの`CommonUtils`は、中身がすべてゴチャ混ぜになっていました。
改善後:責務を分離したクラス設計
「メッセージを表示する責務」を持つクラスとして書き換えます。
‘ 【優良な設計】インスタンス化可能なサービスクラス
Public NotInheritable Class ApplicationMessenger
‘ 外部から変更されない読み取り専用のプロパティ
Private ReadOnly _version As String
‘ コンストラクタで必要な依存関係(バージョン)を受け取る
Public Sub New(ByVal version As String)
_version = version
End Sub
‘ インスタンスメソッドとして定義する
Public Sub ShowMessage(ByVal message As String)
MsgBox(“[” & _version & “] ” & message)
End Sub
End Class
どう使うのか?(呼び出し側のコード)
使う側では、一度オブジェクトを生成(`New`)してから使います。
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
‘ 1. サービスのインスタンスを生成(バージョンを注入)
Dim messenger As New ApplicationMessenger(“1.0.0”)
‘ 2. メソッドを呼び出す
messenger.ShowMessage(“処理が完了しました”)
End Sub
End Class
「あれ? わざわざ `New` するの手間じゃない?」と思いましたか?
その一手間こそが、「誰が・いつ・どんな状態でその処理を使っているか」を明確にする防壁なのです。この形にしておけば、将来的に「テスト用のダミーのバージョンを渡してテストする」といったことが容易にできるようになります。
—
4. クラスへ移行すべき「判断基準」
すべてのModuleを明日から一斉に消す必要はありません。以下の基準に当てはまったら、即座にクラス(あるいはSharedメソッドを持つユーティリティクラス)へリファクタリングを検討してください。
| 判断ポイント | Moduleのままにする? | クラスに移行する? |
| :— | :— | :— |
| 状態(変数)を持つか? | 持たない(純粋な計算ロジックのみ) | 持つ場合は絶対クラスへ! |
| 外部リソース(DB、ファイル等)に依存するか? | 依存しない | 依存するならクラスへ(テストのため) |
| アプリ内で状態が変化するか? | しない | するならクラス(インスタンス)へ |
※どうしても状態を持たない純粋な関数群(例:文字列のトリムや数学的な計算など)をまとめたい場合は、インスタンス化できない`NotInheritable Class`に`Public Shared`メソッドとして定義するのが、VB.NETにおけるモダンなベストプラクティスです。
—
まとめ:ここをクリアすれば基本はバッチリ!
いかがでしょうか?
VB.NETの`Module`は、手軽に動くプログラムを作るための「麻薬」のようなものです。小さいうちは良いですが、コードが膨らんだ瞬間に牙を剥きます。
- Moduleの乱用は、グローバル変数と副作用の温床になり、テストを難しくする。
- 「状態」や「依存関係」を持つ処理は、必ずクラスとしてカプセル化する。
- インスタンス化(`New`)の手間を惜しまないことが、保守性の高い強いコードを作る第一歩。
この考え方が頭に入っていれば、マクロの記録やVBAの延長から完全に脱却し、胸を張って「プロのVB.NETエンジニアだ」と言えるようになります。
オブジェクト指向の波を乗りこなし、メンテンスしやすく美しいコードを一緒に書いていきましょう!あなたのコーディングライフを応援しています。
