疎結合な画面遷移の極意:グローバル変数の呪縛から解き放つVB.NET設計論
諸君、現場で「フォーム間のデータ受け渡し」を実装する際、`Public Shared` の変数や、フォームのインスタンスを無理やり別フォームに渡してコントロールを直接参照するようなコードを書いていないか?
もしそうなら、それは即刻やめるべきだ。その設計は、将来の保守において「変更のたびにシステム全体が崩壊する」という悪夢を約束するチケットに他ならない。
今日は、VB.NETによるWindows Forms開発において、オブジェクトのライフサイクルを尊重し、疎結合かつ堅牢な画面遷移を実現するための「極限の設計」について説く。
—
1. なぜ「直接参照」がシステムを殺すのか
VB.NETの初心者や、VBAから移行したばかりのエンジニアが陥る最大の罠は、フォームを「ただの画面」として扱い、コントロールを「どこからでも触れる変数」として扱うことだ。
しかし、真のアーキテクトはフォームを「データと振る舞いをカプセル化した一つのオブジェクト」として見る。呼び出し元が呼び出し先のコントロール(TextBox等)を直接操作した瞬間、UI構成が変わるたびに呼び出し元のコードまで修正が必要になる。これが依存の連鎖だ。
—
2. プロパティ公開による「契約」の締結
データを渡す際は、必ず呼び出し先フォームに「公開プロパティ」を用意し、それを介してデータを授受せよ。これは呼び出し元と呼び出し先の間で交わされる「契約」である。
実装例:データ受取用プロパティ
Public Class EditForm
‘ 外部から直接コントロールを触らせない。
‘ 必要なデータのみをプロパティとして公開する。
Private _targetId As Integer
Public Property TargetId As Integer
Get
Return _targetId
End Get
Set(value As Integer)
_targetId = value
‘ ここでデータロード等のトリガーを引く
LoadData(_targetId)
End Set
End Property
Private Sub LoadData(id As Integer)
‘ DBアクセスやAPI呼び出しはここで行う
End Sub
End Class
—
3. イベント駆動による「疎結合」なコールバック
問題は逆方向だ。「子画面での操作結果を親画面に通知したい」とき、多くは親フォームの参照を子に渡し、メソッドを呼ぼうとする。これはオブジェクトの循環参照を招き、最悪の場合、メモリリークの温床となる。
ここで使うべきはイベント(`Event` / `RaiseEvent`)だ。子画面は「結果が出た」という事実を発するだけであり、誰がそれを受け取るかを知る必要はない。
実装例:イベントによる通知
‘ 子画面側(EditForm)
Public Class EditForm
‘ 処理完了を親に通知するためのイベント
Public Event DataUpdated(ByVal sender As Object, ByVal e As EventArgs)
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
‘ 保存処理…
‘ 完了通知を発行
RaiseEvent DataUpdated(Me, EventArgs.Empty)
End Sub
End Class
—
4. メモリ管理とライフサイクルの徹底
Windows Formsにおいて、`Close()` を呼んでもオブジェクトは即座に消滅しない。ガベージコレクタ(GC)に委ねるのが基本だが、特に非管理リソース(GDI+ハンドルやWindows API呼び出しの結果)を扱う場合、`IDisposable` の理解が必須だ。
究極のクリーンアップ手法
複数フォームを扱う際は、`Using` ステートメントで囲むか、明示的に `Dispose()` を呼ぶ癖をつけろ。特にモードレスフォームを頻繁に開閉する場合、メモリリークは「静かなる殺人者」となってシステムを蝕む。
Using subForm As New EditForm()
subForm.TargetId = 100
‘ イベントハンドラを登録
AddHandler subForm.DataUpdated, AddressOf Me.OnDataUpdated
If subForm.ShowDialog() = DialogResult.OK Then
‘ 正常終了時の処理
End If
‘ イベントの解除(重要:メモリリークを防ぐ)
RemoveHandler subForm.DataUpdated, AddressOf Me.OnDataUpdated
End Using ‘ ここで確実にDisposeが呼ばれる
—
5. チーフアーキテクトからの提言
システムがレガシーであればあるほど、こうした「規律」が重要になる。
1. コントロールは `Private` を貫け: `Public` なコントロールなど存在してはならない。
2. イベントは「報告」、プロパティは「命令」: この役割分担を徹底するだけで、コードの可読性は劇的に向上する。
3. Windows APIの境界を意識せよ: `P/Invoke` を多用するような高度な制御が必要な場合でも、そのロジックをフォームクラス内に閉じ込めるな。別の「サービス層」クラスに逃がし、フォームを薄く保て。
UIは頻繁に変わるものだ。しかし、データ層やロジック層の設計が堅牢であれば、UIの刷新など恐るるに足らない。
もし、貴殿のシステムがスパゲッティコードの海に沈んでいるなら、まずはこの「プロパティとイベントによる境界線」を引くことから始めてほしい。それが、レガシーから脱却し、真のエンジニアリングへと至る唯一の道である。
健闘を祈る。
