社員ブログ
HOME  社員ブログ
ホーム > Oracle, VB, Windows, 社員:プリースト > VB6によるアーキテクチャ設計 その1 - VB6でオブジェクト指向プログラミングはどこまでできるか?

VB6によるアーキテクチャ設計 その1 - VB6でオブジェクト指向プログラミングはどこまでできるか?

2014年03月16日 12時44分28秒

こんにちは。プリーストです。

今担当しているお仕事では、開発言語にVB6、DBにOracleを使用しています。
VB6!?と思っていましたが、やってみるとこれが割と綺麗に書けたので思考の流れとアーキテクチャ設計指針をここにまとめておこうかなと思いました。
VB6は1998年リリースとのことですが、その時代としてはかなり先進的で画期的な言語だったのではないでしょうか。Windows95、98といいその頃のMicrosoftはすごかったんですね~。

長くなるのでとりあえず、第一回目では概要と方針を書いてみます。

何か参考になれば嬉しいです。

まずは、求められる非機能要件の整理からです。
・現行システムはVB6で作成したクライアントプログラムとOracleのストアドプロシージャで構成される2階層C/Sシステム
・開発するのは割と小規模のサブシステム
・DB操作はほとんどOracleのストアドプロシージャで行うが、保守時のデバッグが難しいことが課題
・使用するのはイントラネット内部のみで通信資源は潤沢
・将来的に.Net等のオブジェクト指向言語でリプレースする可能性がある

お客様に相談して頂いたのがやはり保守時のデバッグ。これがストアドプロシージャで記述してあるとやりづらいとのこと。
VB6のソースコードも読みづらく、保守効率が悪いというよくある相談をされました。
そして、将来的には.Netでのリプレースも考えているとのこと。

そこで、デバッグがし易いようにSQLは全てVB6のソースコード上に記述しVisual Studioからデバッグできるようにしました。

VB6の記述方法も、将来的な.Net化を睨んで、処理を単にサブプロシージャに分けて書き下すVB6の一般的なVB6の書き方ではなく、オブジェクト指向的な責務による分割方法にし、オブジェクト指向の一連の処理が多数のクラスに記述されている処理を飛びまわる感覚に慣れて頂こうと思うがどうでしょう?という提案をしました。

オブジェクト指向的な書き方をしてきちんと問題を分割して記述しておくことは、なるべく変更に強い構造にしておいて、プロジェクト終盤でよく発生しがちな時間が無い中のユーザからの修正要求に対応できるようにしておこうという狙いもありました。


クライアントプログラムからSQLを発行することは、通信量と通信回数が増えるので一般的にはやってはいけないことですが、そこはイントラネットで通信帯域にはかなり余裕があり、このシステムをを使用する端末台数も限られているとのことなのでSQLをクライアントのコードに埋め込んで実装してみようということになりました。

そして、将来的なリプレースの時には、クライアントプログラムから要求を受け付けるサーバプログラムを作成し、SQLを埋め込んだモジュールをサーバプログラムとして切り離せば通信量の問題も解決されるはずです。
埋め込みSQLは文字列なのでコンパイラによるチェックができないのが難点ですが、そこは妥協してしまうことにしました。

基本方針は、
・現行システムはVB6→ストアドプロシージャ→Oracleの構成だが、新規サブシステムはVB6+SQL→Oracleの構成で構築
・なるべくオブジェクト指向的な書き方を目指してカプセル化と処理の共通化を意識する
とこんな感じで作り始めることにしました。

次にやりながら気づいたVB6の特徴、主に制限事項について書きたいと思います。
1. クラスを定義できる。ただし、提供される機能はカプセル化のみで継承、ポリモーフィズムはサポートされない。
2. モジュールには変数と静的なプロシージャのみ定義できる。静的クラスのような使い方ができる。
3. Formはクラスであり、newしてインスタンスを生成できる。インスタンス化しなくても使用できる。
4. try~chatchは無いが、On Error~を用いることで例外補足が可能。
5. 自分で定義した構造体をプロシージャの引数に取ることはできない。
6. 要素数0の配列を定義できない。
7. コレクションが使用できる。速度は配列より速いとのこと。
8. コレクションはJavaのジェネリクスのように要素の型を指定できない。コレクションを引数にするとプロシージャ側から見ると何が格納されているかは保証されない。
9. 10進数の計算で精度が必要な場合には、Variant型の変数にCDec関数で数値をDecimal型に変換して格納する。Decimal型の変数は定義できない。

とこんな感じです。1998年という開発された時代にしては高機能な言語ですが、使い込んでみると中途半端な感じがします。
でもおそらくその時代に受け入れられる為に機能を抑えてわかり易さ、単純さを優先したということもあるかもしれません。
そしてMicrosoftのその戦略は見事当たったと言えるのでしょう。


まずは、1、2のオブジェクト指向的な書き方をどうやって実現したかをポイントを絞って書きます。

・データベース操作やExcelファイル操作等、使用する資源の初期化・終了処理が必要な役割は使用する資源(Oracle、Excel)毎にクラスで定義し、インスタンスの破棄時に確実に資源の解放を行います。

・Formは必ずnewしてFormのインスタンスを生成しインスタンスを表示します。こうすることでForm表示時に必ず初期化処理が呼び出されることを保証します。

・継承を用いた処理の抽象化と共通化はできないので以下のような対応を行い、各場面で固有の振る舞いと、共通の処理を分けます。
    共通クラスを作成する時に呼び出し側の機能・画面毎に変化する情報は全てクラス変数として定義する。
    クラス内部の処理はそのクラス変数を使用してクラスの抽象度を高く保ち、余計なif文を書かない。
    呼び出し側毎に変化する情報をクラス変数にセットするのは呼び出し側のモジュールに定義したプロシージャに任せる。

こうすると、呼び出し側の機能・画面毎にプロジェクトを分けてビルドすることで、機能・画面毎に共通クラスの振る舞いを変えることができます。呼び出し側毎に変化する情報をセットするプロシージャはクラスの初期化処理に呼び出すことにし、そのプロシージャを実装したモジュールがプロジェクト内に存在しない場合にはコンパイルエラーが発生するようにします。

実際には共通クラスから呼び出されるプロシージャはどのモジュールにも定義でき、継承を使用した場合のように制限を設けることはできませんが、共通クラスの呼び出し部分にどのモジュールに定義するかをコメントで記載し、開発者にパターン化した実装を促します。

そのサンプルコードは以下となります。
サンプルコードに採用しているコーディング規約やエラー処理の記述方法、VB6の他の特徴に対する対応などについてはまた今度書こうかなと思います。

——————————————————————————–
・共通クラスの実装
[CommonClass.cls]
Private Sub Class_Initialize()
   On Error GoTo ErrorHandler
    
    ‘ 初期化処理
    ‘ 現在のインスタンスを初期化関数に渡して、各場面に固有の情報で初期化する。
    ‘ 下記プロシージャは各プロジェクト(EXE)毎に用意する共通モジュールに定義する。
    ‘ 例) XxxxXxxxModule.bas 参照
    Call subInitializeObjectMembers(Me)
    
ExitHandler:
    ‘ 正常終了時共通プロシージャ
    Call subCommonExitProcces
    Exit Sub

ErrorHandler:
    ‘ 異常終了時共通プロシージャ
    Call subCommonErrorProcces
        
End Sub
    :
    :
・クラス変数に値をセットするプロシージャ
Public Sub subSetValueToMembers(….)
    On Error GoTo ErrorHandler
    
    ’ここでクラス変数に値をセット

ExitHandler:
    ‘ 正常終了時共通プロシージャ
    Call subCommonExitProcces
    Exit Sub

ErrorHandler:
    ‘ 異常終了時共通プロシージャ
    Call subCommonErrorProcces
        
End Sub


——————————————————————————–
・各プロジェクト毎の呼び出し側機能・画面モジュールで定義
[XxxxXxxxModule.bas]
Public Sub subInitializeObjectMembers(objCommonClass As CommonClass)
    On Error GoTo ErrorHandler
    
    ‘ ここでクラス変数設定プロシージャを呼び出す
    objCommonClass.subSetValueToMembers(固有の値1, 固有の値2, 固有の値3, …)
    
ExitHandler:
    ‘ 正常終了時共通プロシージャ
    Call subCommonExitProcces
    Exit Sub

ErrorHandler:
    ‘ 異常終了時共通プロシージャ
    Call subCommonErrorProcces
        
End Sub

——————————————————————————–

長くなるので今回はこの辺で。。

それではまた♪


Oracle, VB, Windows, 社員:プリースト , ,