【実務・中級編】オブジェクト変数の初期化忘れを防ぐ:クラスのInitializeイベント活用法 – Excel VBA解析バイブル

スポンサーリンク

オブジェクト変数の初期化忘れを防ぐ:クラスの Initialize イベント活用法

こんにちは。チーフアーキテクトの私だ。
これまで数々の巨大なExcel業務自動化ツール、いわゆる「VBAモンスターシステム」のコードレビューを行ってきた。その中で、幾度となく目にしてきた悪夢のようなバグがある。

それが、「オブジェクト変数の初期化忘れ(実行時エラー 91: オブジェクト変数または With ブロック変数が見つかりません)」だ。

‘ ありがちな悲劇のコード
Dim db As clsDatabase
‘ Set db = New clsDatabase ‘ ←これを書き忘れた!
db.Connect ‘ ここで無慈悲なエラー 91 が発生し、ユーザーの作業データが吹き飛ぶ

開発者自身がテストしている時は `New` を書き忘れない。しかし、現場の担当者に引き渡し、運用が始まってから「特定のエッジケースでこのエラーが出る」という報告が上がる。この絶望感は何とも言えないものだ。

プログラミング初心者であれば「気をつければいい」で済ませるかもしれない。しかし、プロのエンジニアは「人間の注意力に依存した設計は、必ず破綻する」という前提でシステムを組む。

今回は、クラスの `Class_Initialize` イベントを極限まで活用し、「インスタンス化された瞬間から100%安全に稼働する」堅牢なクラス設計の極意を伝授しよう。

なぜ「使う側のNew」に依存してはいけないのか?

VBAで独自のクラス (`cls` モジュール) を設計する際、私たちはついつい以下のようなコードを書きがちだ。

‘ 呼び出し側 (Standard Module)
Sub Main()
Dim config As clsConfig
Set config = New clsConfig

‘ プロパティに値をセットしていく
config.FilePath = “C:\Data\settings.json”
config.Load
End Sub

このアプローチの何が問題か?
それは、「このクラスを使うプログラマ(あるいは未来の自分)が、正しくプロパティを設定し、初期化メソッドを呼び出す手順を知っていること」を強制している点にある。

もし、ある開発者が `config.Load` を呼び忘れたらどうなるか? 内部のファイルパスは空のままで動き、予期せぬファイルI/Oエラーを引き起こす。

解決策:クラス自身に「生まれた瞬間」の責任を持たせる

オブジェクト指向の基本に立ち返ろう。オブジェクトとは、データと振る舞いをカプセル化した「自律した存在」であるべきだ。
「生まれ落ちた瞬間(`New`された瞬間)から、不完全な状態で存在することを許さない」。これを実現するのが `Class_Initialize` イベントだ。

実践:ファイル・DB連携を例にした堅牢なクラス設計

ここでは、実務で最も需要が高い「外部設定ファイル(またはDB接続)を読み込み、安全に保持するクラス」を例に、プロダクションコードを示す。

1. クラスモジュール:`clsAppConfig` の実装

VBAのクラスモジュールに `clsAppConfig` と名前をつけ、以下のコードを貼り付けてほしい。

Option Explicit

‘ 内部保持用プライベート変数
private m_IsInitialized As Boolean
private m_ConnectionString As String
private m_TimeoutSeconds As Long

‘ 読み取り専用プロパティ(外部から勝手に書き換えられないようにする)
Public Property Get IsInitialized() As Boolean
IsInitialized = m_IsInitialized
End Property

Public Property Get ConnectionString() As String
‘ 未初期化状態でのアクセスをブロック
If Not m_IsInitialized Then
Err.Raise 9999, “clsAppConfig”, “致命的エラー: クラスが正しく初期化されていません。”
End If
ConnectionString = m_ConnectionString
End Property

Public Property Get TimeoutSeconds() As Long
TimeoutSeconds = m_TimeoutSeconds
End Property

‘ ==========================================================
‘ コンストラクタ (Class_Initialize)
‘ ==========================================================
Private Sub Class_Initialize()
‘ 1. デフォルト値の安全なフォールバック設定
m_TimeoutSeconds = 30
m_IsInitialized = False

‘ 2. 自動初期化プロセスの実行(レジストリ、INIファイル、DBからの自動ロード)
On Error GoTo ErrorHandler

‘ 例:ここで環境に応じた設定を自動読み込みする
Call LoadConfigurationsFromFile

m_IsInitialized = True
Exit Sub

ErrorHandler:
‘ 初期化失敗時はフラグを落としたままにする
m_IsInitialized = False
Debug.Print “[Critical] clsAppConfig の初期化に失敗しました: ” & Err.Description
‘ 必要に応じてログ出力やエラー伝播を行う
End Sub

‘ ==========================================================
‘ 内部初期化ロジック(プライベートメソッド)
‘ ==========================================================
private Sub LoadConfigurationsFromFile()
‘ 実務を想定したダミー処理:本来はここでテキストファイルやDBから設定を取得する
‘ 例として固定値をセット
m_ConnectionString = “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Production\Master.accdb;”

‘ 万が一設定ファイルが存在しない場合のガード
‘ If Dir(“C:\config.ini”) = “” Then Err.Raise 53, , “設定ファイルが見つかりません”
End Sub

‘ ==========================================================
‘ デストラクタ (Class_Terminate)
‘ ==========================================================
Private Sub Class_Terminate()
‘ リソースの解放(ファイルハンドル、DBコネクションのクローズなど)
Debug.Print “clsAppConfig は正常に破棄されました。”
End Sub

この設計の圧倒的なアドバンテージ

1. 「呼ぶ順番を間違えようがない」
呼び出し側は、ただ `New` するだけで、勝手にファイル読み込みとデフォルト値のフォールバックが完了している。
2. 「不正な状態でのアクセスを物理的に遮断」
万が一初期化に失敗した場合や、手順が抜けた場合でも、`Property Get` 内の `If Not m_IsInitialized Then` ガードが作動し、即座に明確なエラー(Err.Raise)を投げる。原因不明のバグに悩まされる時間がゼロになる。
3. 「デストラクタ (`Class_Terminate`) による後始末の自動化」
オブジェクトがスコープを抜ける、あるいは `Set obj = Nothing` された瞬間にメモリやリソースの解放処理を走らせられる。メモリリークやファイルのロック残りを完璧に防ぐ。

呼び出し側のコード(標準モジュール)

このクラスを使う側のコードは、驚くほどシンプルになる。余計な初期化メソッドの呼び出しは一切不要だ。

Sub ExecuteBusinessProcess()
‘ 宣言と同時に(あるいは直後に)インスタンス化
Dim config As clsAppConfig
Set config = New clsAppConfig

‘ すでに初期化済みなのを確認して安全に利用
If config.IsInitialized Then
MsgBox “接続文字列: ” & config.ConnectionString, vbInformation, “初期化成功”
Else
MsgBox “システムの初期化に失敗しました。管理者に連絡してください。”, vbCritical
End If

‘ スコープを抜ける、またはここで解放
Set config = Nothing
End Sub

チーフアーキテクトからの実務アドバイス

VBAは、他のモダン言語(C#やJavaなど)に比べてオブジェクト指向のサポートが「おっとり」している。そのため、ついつい手続き型のベタ書きコード(標準モジュールにプロシージャを並べるスタイル)に逃げがちだ。

しかし、業務自動化ツールが大規模化・複雑化したとき、命を救うのは「堅牢なカプセル化」「ライフサイクルの自動管理」に他ならない。

  • `New` した瞬間に何をやらせるべきか?
  • インスタンスが消滅する間際に何を片付けるべきか?

この2点を `Class_Initialize` と `Class_Terminate` に的確にデザインし込むだけで、あなたの書くVBAコードの信頼性はプロのプロダクションレベルへと劇的に進化する。

「人がうっかりミスをする前提」のコードから、「システム自体が誤用を許さない」高次元の設計へ。
今日の業務から、ぜひこのパターンを導入してみてほしい。

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