
こんにちは、サポート部の Yama-chan です。前回のブログでは PowerBuilder のオブジェクト指向プログラミングの 3 大要素 (継承、カプセル化、多態性) のカプセル化について、サンプルを交えながらご紹介しました。
今回は PowerBuilder における多態性 (Polymorphism : ポリモーフィズム) について、解説したいと思います。
オブジェクト指向プログラミングの多態性は、「同じ名前の関数やイベントが、対象となるオブジェクトに応じて異なる振る舞いをする能力」と定義されています。
PowerBuilder では、多態性を活用することでアプリケーション全体で一貫したインターフェースを提供でき、コードの再利用性と保守性を高めることができます。
PowerBuilder では、多態性を大きく 2 つ、操作の多態性と包含的多態性に分類しています。
これは、関連のない別々のオブジェクトが、同じ名前の関数を定義している状態を指します。 例えば、「データを印刷する」という処理に対して、Print() という関数を定義するとします。
| DataWindow コントロール | Window |
| dw_1.Print() | w_main.Print() |
| データウィンドウの内容を印刷する | ウィンドウの画面キャプチャを印刷する |
これらは全く異なるオブジェクトですが、開発者は「印刷=Print」という共通の操作名を使うことができます。これにより、オブジェクトの種類を問わずに直感的に操作の関数を呼び出せます。
一般的にオブジェクト指向で「多態性」と言うと、こちらの包含的多態性を指す場合が多いです。これは継承関係にあるオブジェクト群において、同じ名前の関数が定義されており、実行時にどのオブジェクトのインスタンスが使われているかによって、呼び出される処理が変わる仕組みです。
今回は、ボーナスの計算について、管理職と一般社員の計算処理を多態性を利用して実装する方法について解説します。
| 祖先クラス | u_employee (関数 CalculateBonus を持つ) |
| 子孫クラスA | u_manager (u_employeeを継承。CalculateBonus をオーバーライドして管理職用の計算を行う) |
| 子孫クラスB | u_staff (u_employeeを継承。CalculateBonus をオーバーライドして一般社員用の計算を行う) |
スクリプト内で、変数の型を祖先クラス (u_employee) として宣言していても、実行時に代入されているインスタンスが u_manager であれば管理職用の計算が、u_staff であれば一般社員用の計算が実行されます。
PowerBuilder で多態性を実装するための具体的な機能はオーバーライド (Overriding) とオーバーロード (Function Overloading) です。それぞれについて、確認してみましょう。
同じオブジェクト内、または継承関係において、関数名は同じですが、引数のリスト (型や数) が異なる関数を定義することです。
例:ログ出力関数のオーバーロード
以下の関数は string 型で文字列を受け取っている場合の処理です。
// 1. 文字列を受け取るバージョン public function integer of_log(string as_message) // 文字列をログファイルに書き込む処理 return 1 end function
以下の関数は long 型でエラーコードを受け取り、メッセージに変換している場合の処理です。
// 2. 数値(エラーコード)を受け取るバージョン public function integer of_log(long al_errorcode) // エラーコードをメッセージに変換して書き込む処理 return 1 end function
注意点 :
グローバル関数はオーバーロードできません。オブジェクトレベルの関数のみが可能です。また、数値型 (Integer と Long など) のオーバーロードは、PowerBuilder の自動型変換により意図しない方が呼ばれる可能性があるため注意が必要です。
オーバーロード (Function Overloading)
同じオブジェクト内、または継承関係において、関数名は同じだが引数のリスト (型や数) が異なる関数を定義することです。 PowerBuilder は、呼び出し時に渡された引数の型と数をチェックし、適切なバージョンの関数を自動的に選択して実行します。
ここでは「包含的多態性」を使った、役職ごとのボーナス計算の実装例を見てみましょう。
1). 祖先クラスの作成 (n_cst_employee)
まず、基本となる従業員クラスを作成し、標準的なボーナス計算ロジックを定義します。
// n_cst_employee の関数: of_calculate_bonus // 引数: decimal adec_salary (基本給) // 戻り値: decimal // 一般社員は基本給の 2ヶ月分 RETURN adec_salary * 2
2). 子孫クラスの作成 (n_cst_manager)
次に、祖先クラスを継承して「管理職クラス」を作成し、関数をオーバーライドします。
// n_cst_manager (n_cst_employeeを継承) // 関数: of_calculate_bonus (祖先と同じ名前・引数で作成) // 管理職は基本給の 4ヶ月分 + 役職手当 RETURN (adec_salary * 4) + 100000
3). 多態性を利用した呼び出し
一般社員用 :
// 変数は「祖先型」で宣言する n_cst_employee lnv_staff // 【ケースA】一般社員として生成 lnv_staff = CREATE n_cst_employee // 結果: 600,000 (30万 * 2) が返る MessageBox("一般社員", lnv_staff.of_calculate_bonus(300000)) DESTROY lnv_staff
管理職用 :
ここがポイントです。変数は祖先型 (n_cst_employee) で宣言しますが、実際に代入するのは子孫のインスタンス (n_cst_manager) です。
// 変数は「祖先型」で宣言する n_cst_employee lnv_staff // 【ケースB】管理職として生成(ここが多態性!) // 変数の型は n_cst_employee のままだが、中身は n_cst_manager lnv_staff = CREATE n_cst_manager // コンパイラは n_cst_employee の関数を見ているが、 // 実行時には n_cst_manager のオーバーライドされた関数が動く // 結果: 1,300,000 (30万 * 4 + 10万) が返る MessageBox("管理職", lnv_staff.of_calculate_bonus(300000)) DESTROY lnv_staff
このように、呼び出し側のコード(of_calculate_bonus を呼ぶ部分)を変えることなく、オブジェクトを入れ替えるだけで異なる計算ロジックを実行できます。これが多態性の威力です。
多態性を利用して関数を呼び出す際、PowerBuilder がいつ関数を探すか (バインディング) は 2 つの方式、静的ルックアップと動的ルックアップがあります。
| 特徴 | 静的ルックアップ (Static) | 動的ルックアップ (Dynamic) |
| 構文 | obj.function() | obj.DYNAMIC function() |
| チェック | コンパイル時に存在チェック | 実行時に存在チェック |
| 速度 | 高速 | 低速 |
| 安全性 | 高い (コンパイルエラーになる) | 低い (実行時、エラーの可能性がある) |
| 条件 | 変数の型 (祖先) に関数が定義されていること | 関数の型に関係なく、実体にその関数があればOK |
推奨するアプローチ :
基本的には静的ルックアップの使用をお勧めします。 上記のサンプルのように、祖先クラスにあらかじめ共通の関数 (空の関数やデフォルト処理) を定義しておけば、子孫クラスでそれをオーバーライドすることで、静的ルックアップのまま多態性を実現できます。
みなさん、PowerBuilder の多態性について一通り確認しましたが、いかがでしたでしょうか。
多態性を活用すると、オブジェクトの種類を意識せずに共通のインターフェースで操作できるようになります。オーバーロードは「引数の違いによる振る舞いの切り替え」、オーバーライドは「継承による上書き」で動作を変更する仕組みです。変数を祖先クラスで宣言し、子孫クラスのインスタンスを代入することで、柔軟にロジックを切り替えられます。
また、パフォーマンスと保守性の観点からは、静的ルックアップ (祖先クラスに関数を定義する方法) を優先することをおすすめします。
多態性を上手に取り入れて、修正に強く、拡張しやすい PowerBuilder アプリケーションを目指していきましょう。
以上、Yama-chan でした。