VB6によるアーキテクチャ設計 その2 - エラー処理と関心事の分離
こんにちは。プリーストです。
前回の投稿からやや時間が空いてしまいましたが、前回に引き続きVB6での開発のお話です。
前回は、主にシステムの前提制約とVB6の特徴、そしてプロジェクトの分割と共通クラスを用いてポリモーフィズムを使用した時のように、該当プロジェクト固有の値を使って共通クラスで処理させる方法を書きました。
VB6によるアーキテクチャ設計 - VB6でオブジェクト指向プログラミングはどこまでできるか?
今回は、以下のVB6の特徴の4番を使用したエラー処理について主に書きたいと思います。
○VB6の特徴(実装中に気づいたもの)
1. クラスを定義できる。ただし、提供される機能はカプセル化のみで継承、ポリモーフィズムはサポートされない。
2. モジュールには変数と静的なプロシージャのみ定義できる。静的クラスのような使い方ができる。
3. Formはクラスであり、newしてインスタンスを生成できる。インスタンス化しなくても使用できる。
4. try~chatchは無いが、On Error~を用いることで例外補足が可能。
5. 自分で定義した構造体をプロシージャの引数に取ることはできない。
6. 要素数0の配列を定義できない。
7. コレクションが使用できる。速度は配列より速いとのこと。
8. コレクションはJavaのジェネリクスのように要素の型を指定できない。コレクションを引数にするとプロシージャ側から見ると何が格納されているかは保証されない。
9. 10進数の計算で精度が必要な場合には、Variant型の変数にCDec関数で数値をDecimal型に変換して格納する。Decimal型の変数は定義できない。
VB6でのエラー処理は一般的に、
Private Sub subXxxxx()
On Error GoTo ErrorHandler
‘ エラーが発生する可能性のある処理
Exit Sub
ErrorHandler:
‘ メッセージ表示などのエラー処理
End Sub
等と書きます。
しかし、エラー処理をプロシージャ毎に書いていたのでは記述が冗長なばかりか、エラーハンドリングに変更を加えたい場合に工数が膨らみます。
そこで、エラー処理を共通化する試みをしました。方法は単純で共通のプロシージャを用いるということです。
○全プロシージャ共通の実装イメージ
Private Sub subXxxxx()
On Error GoTo ErrorHandler
‘ エラー情報クリア
Call subClearError
‘ エラーが発生する可能性のあるサブプロシージャ
Call subYyyyy()
If gobjError.ErrorCode <> “” Then GoTo ErrorHandler
:
:
:
ExitHandler:
Call subEndProcces
Exit Sub
ErrorHandler:
Call subErrorProcces
End Sub
○エラーを発生させたい処理
Private Sub subYyyyy()
On Error GoTo ErrorHandler
‘ エラー情報クリア
Call subClearError
‘ エラー処理
If (……) Then
gobjError.ErrorCode = “ERR001”
gobjError.ErrorMessage = “エラーメッセージ”
GoTo ErrorHandler
End If
‘ 途中で正常終了したい場合
If (……) Then
GoTo ExitHandler
End If
:
:
ExitHandler:
Exit Sub
ErrorHandler:
Call subErrorProcces
End Sub
というように書きます。ここで注意すべき点は、
・サブプロシージャを呼び出した後は必ずエラー有無の確認を行う。
・エラー情報は独自のオブジェクトで管理する。
・正常時、異常時両方にラベルを定義し、特に処理がなくても全プロシージャでテンプレートのように使用する。
○エラー情報クラス
‘ エラー情報保持クラス
‘ EXE全体のエラー管理及びエラーリスト処理クラスのデータとして用いる。
‘ 構造体で定義するとコンパイルエラーとなる為、クラスで定義する。
Public ErrorCode As String ‘ エラーコード
Public ErrorMessage As String ‘ エラーメッセージ
:
:
○グローバル定義
‘ 共通エラー情報クラスオブジェクト(アプリケーションで唯一のインスタンス)
Public gobjError As New ErrorInfo ‘ 共通エラー情報
○クリア関数
エラー情報インスタンスクリア
Public Sub subClearError()
‘ メッセージクリア
gobjError.ErrorCode = “”
gobjError.ErrorMessage = “”
End Sub
○エラー処理関数
実行時エラーなどを独自に定義したメッセージに変換する処理などを行う。
Public Sub subErrorProcces()
‘ エラー設定処理でOn Error句を用いるとErrオブジェクトがクリアされるので用いない。
‘ 実行時エラー変換
Select Case Err.Number
Case 70
gobjError.ErrorCode = “ERR002”
Case XX
gobjError.ErrorCode = “ERRXXX”
:
:
Case Else
End Select
If gobjError.ErrorCode <> “” Then Exit Sub
‘ Runtimeエラーメッセージ取得
Call subSetRuntimeError
End Sub
こうすることで、VB6であってもエラー処理をビジネスロジックと分離し管理できるようになります。これはAOP(Aspect Oriented Programming)が生まれた目的と同じで、ソフトウェア全体を横断する関心事を分離するものです。
もう一つのエラー処理の方法としては、エラーとしたい場合に「Err.Raise 9999(独自に定義した数値)」などとして実行時エラーを発生させ、最上位の呼び出し元で補足して処理するという方法もあります。
その場合、On Error GoTo~などのエラー処理ルーチンを終了すると、Errオブジェクトがクリアされてしまうので、補足するのはあくまで最上位の呼び出し元のみとする必要があります。
これだと、記述量が少なくシンプルになります。ただErrオブジェクトをクリアしないように気をつける必要があるので、小規模案件や少人数のプロジェクト向きかもしれません。
このように、共通クラス定義や広域変数でのインスタンス保持、プロジェクト分割、チーム内のルールや規約などを組み合わせることで、モダンな言語が備える問題の分割や処理の共通化の仕組みをある程度実現できます。
言うまでもなく、言語仕様に元々分割、共通化の仕組みが備わっている方がコードの統括、見通しの良さ共に良いのであくまでもなんらかの事情によりVB6の採用が最適解である場合のコード整理の仕方ということで参考になればと思います。
エラー処理については以上です。また他のトピックについても書いていきたいと思います。
それではまた♪
最近のコメント