疎結合という名の「不可逆な正義」:VB.NETで構築する永続的な業務システム基盤
VB.NETを単なる「VBAの延長」と捉えている者は、この言語が持つ.NETランタイムとの親和性、そしてCLR(Common Language Runtime)が提供するメモリ管理能力をドブに捨てているに等しい。
業務システムは、一度構築すれば10年は動く。明日、仕様変更の嵐が吹いても揺るがない「疎結合な設計」こそが、我々アーキテクトが唯一守るべき防波堤だ。今回は、インターフェイスと抽象クラスを武器に、ガチガチに固められたレガシーを、拡張性のある現代的アーキテクチャへと昇華させる極意を伝授する。
—
1. 抽象クラスとインターフェイスの「境界線」を定義せよ
多くのエンジニアが「なんとなく」使い分けているが、ここに設計の生死がかかっている。
- 抽象クラス (MustInherit): 「is-a(~である)」の関係。基底クラスとしての振る舞い(共通メソッドの実装や状態管理)を共有させる。Windows API呼び出しのラッパーや、ログ出力基盤といった「基底的機能」に用いる。
- インターフェイス (Interface): 「can-do(~ができる)」の関係。契約(Contract)である。実装の詳細を隠蔽し、外部連携やプラグイン構造を実現する。
実践的アーキテクチャ例
‘ 外部システム連携を抽象化するインターフェイス
Public Interface IExternalSystemAdapter
Sub SendData(data As String)
Function GetStatus() As Boolean
End Interface
‘ 共通機能を担う抽象クラス(Windows API呼出等を内包)
Public MustInherit Class BaseSystemComponent
Implements IDisposable
‘ メモリ最適化:アンマネージリソースの解放を強制するテンプレート
Protected disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
‘ マネージリソースの解放
End If
‘ ここにWindows API等で確保したハンドル解放処理を記述
disposedValue = True
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
—
2. Windows APIとの「危険な橋」を渡る作法
VB.NETでレガシーなWindows API(`kernel32.dll`等)を呼ぶ際、最も重要なのは「例外をインターフェイス層で封じ込める」ことだ。APIはCLRの管理外であり、メモリリークやスタック破壊の温床となる。
必ず`SafeHandle`クラスを活用し、インターフェイスを通じてAPI層をカプセル化せよ。
‘ 良い設計:APIの生呼び出しを抽象クラス内に隠蔽する
Public Class Win32NativeWrapper
Inherits BaseSystemComponent
Private Shared Function GetTickCount() As UInteger
End Function
Public Function GetSystemUptime() As UInteger
‘ API呼び出しの戻り値を型安全に変換して返す
Return GetTickCount()
End Function
End Class
—
3. メモリ最適化:ガベージコレクタを信じるな
.NETのGC(ガベージコレクタ)は優秀だが、業務システム特有の「大量のExcel操作」や「巨大なログファイル解析」においては無力だ。`IDisposable`を実装し、`Using`ステートメントを強制する設計こそが、メモリ枯渇によるシステムクラッシュを防ぐ唯一の手段である。
シニアの鉄則:
- Finalizeを過信しない: 巨大なオブジェクトは`Dispose`で即座に解放せよ。
- Marshalの使用に注意: `Marshal.AllocHGlobal`等でメモリを確保した場合は、必ず`try…finally`で解放を担保せよ。
—
4. 継承より「コンポジション」を選べ
VB.NETのコードが保守不能になる最大の理由は、深い継承階層だ。`Inherits`を多用すれば、基底クラスの些細な変更がシステム全体を破壊する。
インターフェイスを用いて機能を「注入(Dependency Injection)」する設計を心がけよ。
‘ 悪い例:継承の地獄
‘ Public Class AdvancedReportGenerator Inherits ReportGenerator …
‘ 良い例:コンポジションによる機能追加
Public Class ReportEngine
Private _formatter As IReportFormatter ‘ インターフェイスを保持
Public Sub New(formatter As IReportFormatter)
_formatter = formatter
End Sub
Public Sub Generate()
_formatter.Format()
End Sub
End Class
—
最後に:伝説のアーキテクトからの助言
VB.NETは「古い」のではない。「使い手が古臭い設計から脱却できていないだけ」だ。
インターフェイスを定義することは、システムの未来を予約することと同義である。もし君が現在、VBAからの移行やレガシー刷新のプロジェクトを率いているなら、まずは「全てのクラスから`Public`なメソッドを排除し、インターフェイスを経由させる」ことから始めてみてほしい。
その先には、仕様変更を恐れず、単体テストが完璧に機能し、メモリ効率が最適化された、美しい業務システムの姿があるはずだ。
コードは嘘をつかない。君が設計した構造が、そのままシステムの寿命になる。魂を込めて、型を定義せよ。
