CLOSE
カプセル化

こんにちは、サポート部の Yama-chan です。

PowerBuilderにおける「カプセル化(Encapsulation)」は、オブジェクト指向プログラミングの3大要素(継承、カプセル化、多態性)の一つであり、堅牢で保守性の高いアプリケーションを構築するために不可欠な概念です。

今回のブログでは、PowerBuilderでのカプセル化の実装方法とそのメリットを解説していきます。

PowerBuilderでアプリケーションを開発する際、ただ動くコードを書くだけではなく、「壊れにくく、修正しやすい」コードを目指していますか?その鍵を握るのが、オブジェクト指向の基本原則の一つである「カプセル化(Encapsulation)」です。

カプセルとは ?

カプセル化とは、オブジェクト内のデータ (変数) とコード (処理) をひとまとめにし、外部からのアクセスを適切に制限することを指します。別名「情報隠蔽 (Information Hiding) 」とも呼ばれます。


PowerBuilder はカプセル化を強制するわけではありませんが、アクセス権 (Access Scope) などの機能を提供することで、開発者が意図的にカプセル化を実装できるよう設計されています。

なぜカプセル化が必要なのか ?

カプセル化を行う主な目的は、「オブジェクトの独立性を高めること」です。

データ保護 オブジェクト内部の状態(インスタンス変数)を、外部のスクリプトから勝手に書き換えられないようにします
インターフェイスの統一 外部からは決められた関数(メソッド)を通してのみ操作させることで、内部ロジックが変更されても外部への影響を最小限に抑えられます。

PowerBuilder での実装テクニック

PowerBuilder でカプセル化を実現するための具体的な機能を見ていきましょう。

1. アクセス権の制御 (Public, Protected, Private)

PowerBuilder では、インスタンス変数や関数に対して以下のアクセスレベルを設定できます。

Public(デフォルト) アプリケーション内のどこからでもアクセス可能。
Protected そのオブジェクト自身と、そのオブジェクトを継承した子孫オブジェクトからのみアクセス可能。
Private そのオブジェクト自身のスクリプトからのみアクセス可能(子孫からもアクセス不可)。

実装のポイント : 完全にカプセル化を行うためには、インスタンス変数は基本的に Private または Protected として宣言し、外部からの直接アクセスを遮断すべきです。

2. きめ細やかなアクセス制御 (PrivateWrite / ProtectedWrite)

PowerBuilder の強力な機能として、変数の「読み取り」と「書き込み」の権限を個別に設定できます。これは非常に有効なカプセル化の手段です。

例えば、外部からは値を参照(Read)させたいが、値を変更(Write)できるのはオブジェクト自身だけにしたい場合、以下のように宣言します。

// 変数宣言の例
Public PrivateWrite integer ii_count

こうすることで、外部のスクリプトは n_cst_object.ii_count の値を読むことはできますが、値を代入しようとするとコンパイルエラーになります。これにより、不正な値の更新を防ぐための検証ロジック (関数) を通すことを強制できます。

3. アクセッサー関数 (Getter / Setter) の作成

変数を Private や Protected に隠蔽した後は、それらにアクセスするための関数 (Object Functions) を用意します。

Setter関数(uf_set_variable) 値をセットする際に、妥当性チェックなどのビジネスロジックを実行してから内部変数を更新します。
Getter関数(uf_get_variable) 内部変数の値を返します。

4. カスタムクラスユーザーオブジェクト (NVO) の活用

カプセル化を最も効果的に活用できるのが、カスタムクラスユーザーオブジェクト (Non-Visual Object : NVO) です。

ビジネスルール、計算処理、データアクセス処理などをNVOに集約 (カプセル化) することで、画面 (Window) 側のコードをシンプルに保つことができます。例えば、消費税計算や手数料計算などのロジックを一つのNVOにまとめ、画面からはそのNVOの関数を呼び出すだけにすれば、将来計算ロジックが変わってもNVOの修正だけで済みます。

また、例外処理 (Try-Catch) を NVO 内に組み込むことで、エラーハンドリングを統一し、呼び出し元 (プレゼンテーション層) とビジネスロジックを分離する設計も推奨されています。

実装例

データと処理を隠蔽するアプローチとして、単一のエントリーポイント (窓口となる関数) を用意する方法があります。

  1. ・インスタンス変数は Private に設定。
  2. ・実際の処理を行う関数も Private または Protected に設定。

開発者はこの Public 関数だけを知っていればよく、内部でどのような複雑な処理が行われているかを気にする必要がなくなります。

実装シナリオ : 売上処理オブジェクト (n_cst_sales_processor)

このオブジェクトは、外部から「注文ID」と「処理タイプ(承認、保留、拒否)」を受け取ります。しかし、実際の「与信チェック」や「在庫引き当て」といった具体的な処理手順は、外部に見せないようにします。

1. 変数の宣言 (Private)

オブジェクトの状態を保持する変数は Private に設定し、外部から直接書き換えられないようにします。

// n_cst_sales_processor のインスタンス変数宣言
// 外部からのアクセスを禁止 (Private)
PRIVATE:
Long il_current_order_id
String is_current_status
Decimal idec_order_amount

上記コードでは 「PRIVATE: 」という記述の下に複数の変数が宣言されていますが、このように記述することで、以降に宣言された変数に対してまとめてアクセス権を指定することができます。

2. 内部処理関数の作成 (Private / Protected)

実際の業務ロジックを行う関数は Private または Protected として定義し、外部スクリプトから直接呼び出せないようにします。

アクセス Private
戻り値 Boolean
// 内部ロジック:与信限度額のチェック(簡略化)
IF idec_order_amount > 1000000 THEN
    // 100万円を超える場合は厳格なチェック(ダミー処理)
    RETURN False
END IF
RETURN True
アクセス Private
戻り値 Integer
// 内部ロジック:在庫の引き当て処理(ダミー処理)
// 成功したら 1 を返す
RETURN 1

3. 公開エントリーポイント関数の作成 (Public)

外部に対して唯一公開する「窓口」となる関数を作成します。引数によって内部で実行する処理を振り分けます。

アクセス Public
引数 al_order_id (Long) 注文ID
as_action (String) 処理内容 (“APPROVE”, “REJECT”, “HOLD”)
戻り値 String (処理結果メッセージ)
// 公開関数:外部からのリクエストを受け付ける単一の窓口
String ls_result
 
// 内部変数のセット
il_current_order_id = al_order_id
// 本来はDBから金額を取得するが、ここでは例としてセット
idec_order_amount = 500000
 
// アクションに応じた処理の振り分け (CHOOSE CASE)
CHOOSE CASE Upper(as_action)
    CASE "APPROVE"
        // 承認処理:内部のPrivate関数を呼び出す
        IF uf_validate_credit() = True THEN
            uf_update_inventory()
            is_current_status = "Approved"
            ls_result = "注文ID: " + String(il_current_order_id) + " は正常に承認されました。"
        ELSE
            is_current_status = "Rejected"
            ls_result = "エラー: 与信枠を超過しています。"
        END IF
 
    CASE "REJECT"
        // 拒否処理
        is_current_status = "Rejected"
        ls_result = "注文ID: " + String(il_current_order_id) + " は拒否されました。"
 
    CASE "HOLD"
        // 保留処理
        is_current_status = "On Hold"
        ls_result = "注文ID: " + String(il_current_order_id) + " は保留ステータスに変更されました。"
 
    CASE ELSE
        ls_result = "エラー: 不明なアクションです。"
 
END CHOOSE
 
RETURN ls_result

ウィンドウ側の開発者は、内部で uf_validate_credit が呼ばれているのか、uf_update_inventory が呼ばれているのかを知る必要はありません。単に「承認してくれ (APPROVE) 」と依頼するだけです。

// ウィンドウのボタンの Clicked イベント
n_cst_sales_processor lnv_processor
String ls_message
Long ll_order_id = 1001
 
// インスタンスの生成
lnv_processor = CREATE n_cst_sales_processor
 
// 単一の公開関数を呼び出し、処理タイプを指定する
// 内部でどのようなチェックが行われているかは意識しなくて良い
ls_message = lnv_processor.uf_process_order(ll_order_id, "APPROVE")
 
MessageBox("処理結果", ls_message)
 
// インスタンスの破棄
DESTROY lnv_processor

まとめ

カプセル化のメリット

仕様変更への強さ 将来、「承認(APPROVE)」のルールが変更され、在庫チェックの前に追加の審査が必要になったとします。その場合でも、修正するのは n_cst_sales_processor の内部(uf_process_order や Private関数)だけで済みます。呼び出し元のウィンドウのコードは一切修正する必要がありません。
不正な操作の防止 uf_validate_credit (与信チェック)は Private なので、外部のプログラマがチェックをスキップして在庫更新を直接呼び出すといった不正なコードを書くことを物理的に防げます。
インターフェースの単純化 オブジェクトを利用する開発者は、uf_process_order という一つの関数さえ知っていればよいため、学習コストが下がります。

このように、PowerBuilder においてアクセス権 (Public / Private) と関数による処理の抽象化を組み合わせることで、堅牢なカプセル化を実現できます。カプセル化は、アプリケーションの寿命を延ばし、バグを減らすための重要なテクニックです。皆さんも、機能を実装する前にカプセル化を意識することで、PowerBuilder アプリケーションの品質は大きく向上するはずです。

以上、Yama-chan でした。

x instagram facebook youtube