現在の形式のコードで、同じ航空会社を表す LCL_CARRIER の複数のインスタンスが存在することが許可されていることがわかります。これを修正するには、特定の運送業者 ID 値に対して 1 つのインスタンスのみを作成できるようにするファクトリメソッドを定義および実装します。
タスク 1: テンプレートのコピー (オプション)
テンプレートクラス /LRN/CL_S4D401_OOS_INTF_USAGE をコピーします。前の演習問題を完了した場合は、このタスクをスキップし、クラス ZCL_##_SOLUTION の編集を続行することができます。
ステップ
クラス /LRN/CL_S4D401_OOS_INTF_USAGE を独自のパッケージのクラスにコピーします (推奨名: ZCL_##_SOLUTION、## はグループ番号)。
Project Explorer で、クラス /LRN/CL_S4D401_OOS_INTF_USAGE を右クリックしてコンテキストメニューを開きます。
コンテキストメニューから Duplicate.... を選択します。
Package 項目にパッケージの名称を入力します。Name 項目に、ZCL_##_SOLUTION (## はグループ番号) と入力します。
Next を選択します。
移送依頼を確認し、Finish を選択します。
コピーを有効化します。
Ctrl + F3 を押してクラスを有効化します。
タスク 2: ファクトリメソッドの定義
クイックフィックスを使用して、ローカルクラス LCL_CARRIER のファクトリメソッドを定義します (推奨名: GET_INSTANCE)。ファクトリメソッドでは、インスタンスを実際に作成する前に、存在チェックおよび権限チェックを実行します。次に、このクラスのインスタンス作成を制限して、ファクトリメソッドの使用を強制します。
ステップ
ローカルクラス LCL_CARRIER で、クイックフィックスを使用してファクトリメソッドを定義します。
ヒント
コンストラクタパラメータは、デフォルトでファクトリメソッドの署名に追加されます。そのため、carrier_id 属性を初期化対象の属性として明示的に選択しないでください。
LCL_CARRIER の定義にナビゲートします。
CLASS … DEFINITION 命令で、lcl_carrier にカーソルを置き、Ctrl + 1 を押して利用可能なクイックフィックスを呼び出します。
利用可能なクイックフィックスの一覧から、Generate factory method create を選択します。
名称項目に GET_INSTANCE と入力します。
初期化する属性を選択セクションで、全選択解除を選択し、終了を選択します。
クイックフィックスで以下のメソッド定義を追加しておく必要があります。
12345
CLASS-METHODS get_instance
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RETURNING
value(r_result) TYPE REF TO lcl_carrier.
... および以下の実装:
123
METHOD get_instance.
r_result = NEW #( i_carrier_id = i_carrier_id ).
ENDMETHOD.
SELECT 命令と AUTHORITY-CHECK 命令を CONSTRUCTOR からファクトリメソッドの実装に移動します。インスタンス化前に必ずチェックを実行してください。
ローカルクラス LCL_CARRIER のメソッド CONSTRUCTOR の実装にナビゲートします。
SELECT 命令と AUTHORITY-CHECK 命令 (システム項目 SY-SUBRC の評価と例外発生を含む) を選択し、Ctrl + C を押してコードスニペットをクリップボードにコピーします。
Ctrl + < を押して、選択した各コード行の前にコメント記号を追加します。
メソッド GET_INSTANCE の実装にナビゲートし、パラメータ r_result の値割当の前にカーソルを置き、Ctrl + V を押してコードスニペットを挿入します。
コードは以下のようになります。
12345678910111213141516171819202122
METHOD get_instance.
SELECT SINGLE
FROM /lrn/carrier
FIELDS concat_with_space( carrier_id, name, 1 ), currency_code
WHERE carrier_id = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
AUTHORITY-CHECK
OBJECT '/LRN/CARR'
ID '/LRN/CARR' FIELD i_carrier_id
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_auth_check_exception.
ENDIF.
r_result = NEW #( i_carrier_id = i_carrier_id ).
ENDMETHOD.
CONSTRUCTOR メソッドの実装から RAISE EXCEPTION 命令を削除したため、CONSTRUCTOR メソッド定義の署名にある 2 つの対応する RAISING オプションを削除します。
LCL_CARRIER の CONSTRUCTOR メソッドの定義にナビゲートします。
以下のようにメソッド定義を調整します。
123456
METHODS constructor
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
.
GET_INSTANCE メソッドの実装に戻ります。SELECT 文で、INTO 句を調整します。インスタンス属性の名前および currency_code ではなく、インライン宣言構造体 (推奨名: details) に結果を格納します。
コードを以下のように調整します。
1234567
SELECT SINGLE
FROM /lrn/carrier
FIELDS concat_with_space( carrier_id, name, 1 ) AS name,
currency_code
WHERE carrier_id = @i_carrier_id
INTO @DATA(details).
運送業者オブジェクトのインスタンス化後、詳細構造の値を新規インスタンスの対応するインスタンス属性に割り当てます。
コードを以下のように調整します。
12345
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
ENDMETHOD.
クイックフィックスを使用して、2 つの例外の発生宣言を追加します。
最初の RAISE EXCEPTION 命令の左側にある電球の警告アイコンを選択して、利用可能なクイックフィックスを呼び出します。
利用可能なクイックフィックスの一覧から、Add raising declaration を選択します。
他の RAISE EXCEPTION 命令に対してこれを繰り返します。
クイックフィックスによって、コードが以下のように調整されている必要があります。
12345678
CLASS-METHODS get_instance
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RETURNING
value(r_result) TYPE REF TO lcl_carrier
RAISING
cx_abap_invalid_value
cx_abap_auth_check_exception.
ローカルクラス LCL_CARRIER のインスタンス化を制限します。インスタンス化がクラス自体内でのみ許可されることを確認してください。
LCL_CARRIER の定義にナビゲートします。
コードを以下のように調整します。
1
CLASS lcl_carrier DEFINITION CREATE PRIVATE.
CONSTRUCTOR メソッドの定義をプライベートセクションに移動します。
注記
これはオプションですが、読みやすくするために実行する必要があります。
メソッド CONSTRUCTOR の METHODS 命令を public セクションから private セクションにコピー & ペーストします。
グローバルクラスの IF_OO_ADT_CLASSRUN~MAIN メソッドで、NEW 式をファクトリメソッド GET_INSTANCE の呼出に置き換えます。
コードを以下のように調整します。
12
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
ローカルテストクラス LTCL_FIND_FLIGHTS のメソッド CLASS_SETUP で、NEW 式をファクトリメソッド GET_INSTANCE の呼出に置き換えます。
コードを以下のように調整します。
12
the_carrier = lcl_carrier=>get_instance( i_carrier_id = some_flight_data-carrier_id ).
グローバルクラスをコンソールアプリとして有効化してテストします。GET_INSTANCE メソッドの実行をデバッグします。
Ctrl + F3 を押します。
F9 を押します。
タスク 3: ファクトリメソッドの拡張
ローカルクラス LCL_CARRIER で、LCL_CARRIER (推奨名: instances) のインスタンスへの参照を保存できる静的属性を定義します。次に、ファクトリメソッドの実装を拡張します。インスタンスの作成後、新しいインスタンスへの参照を属性インスタンスに保存します。要求された運送業者 ID のオブジェクトがまだインスタンスで参照されていない場合にのみ、新しいインスタンスを作成します。
ステップ
ローカルクラス LCL_CARRIER で、クイックフィックスを使用して、このクラスのテーブルデータ型を登録します (推奨名: tt_carriers)。
ローカルクラス LC_CARRIER の定義にナビゲートします。
CLASS … DEFINITION 命令で、lcl_carrier にカーソルを置き、Ctrl + 1 を押して利用可能なクイックフィックスを呼び出します。
利用可能なクイックフィックスの一覧から、Generate table type for lcl_carrier を選択します。
新しい TYPES 命令で、tab を tt_carriers に置き換えます。
TYPES 命令は以下のようになります。
12
TYPES: tt_carriers TYPE STANDARD TABLE OF REF TO lcl_carrier
WITH DEFAULT KEY.
先ほど作成したテーブルデータ型に基づいてプライベート静的属性を定義します (推奨名: instances)。
PRIVATE SECTION 命令まで下にスクロールします。
コードを以下のように調整します。
12
PRIVATE SECTION.
CLASS-DATA instances TYPE tt_carriers.
ファクトリメソッド GET_INSTANCE の実装を調整します。インスタンス化の後、新しいインスタンスへの参照をインスタンス属性に追加します。
コードを以下のように調整します。
123456
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
APPEND r_result TO instances.
ENDMETHOD.
インスタンス化の前、ただし存在チェックおよび権限チェック後に、属性インスタンスから読み込まれた単一レコードを実装します。属性 carrier_id の値がインポートパラメータ i_carrier_id の値と等しいインスタンスを読み込みます。リターンパラメータ r_result に結果を保存します。
ヒント
非構造化行データ型の内部テーブルでは、擬似コンポーネント table_line を使用してデータをアドレス指定することに注意してください。
コードを以下のように調整します。
1234567891011
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_auth_check_exception.
ENDIF.
r_result = instances[ table_line->carrier_id = i_carrier_id ].
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
APPEND r_result TO instances.
単一レコード読込アクセス、インスタンス化、属性への値割当、および TRY...ENDTRY 制御構造。
コードを以下のように調整します。
123456789
TRY.
r_result = instances[ table_line->carrier_id = i_carrier_id ].
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
APPEND r_result TO instances.
ENDTRY.
システム例外 CX_SY_ITAB_LINE_NOT_FOUND が発生した場合にのみ、インスタンスを登録し、それを属性インスタンスに追加してください。
コードを以下のように調整します。
123456789
TRY.
r_result = instances[ table_line->carrier_id = i_carrier_id ].
CATCH cx_sy_itab_line_not_found.
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
APPEND r_result TO instances.
ENDTRY.
グローバルクラスで、メソッド IF_OO_ADT_CLASSRUN~MAIN の実装を調整します。GET_INSTANCE ファクトリメソッドへの 2 回目の呼出を、既存の呼出と同じ入力で、結果に対して別の参照変数を使用して実装します (推奨名: carrier2)。
コードを以下のように調整します。
12
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
DATA(carrier2) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
グローバルクラスをコンソールアプリとして有効化してテストします。コードをデバッグして、ファクトリメソッドの 2 つの呼出で LCL_CARRIER の同じインスタンスが返されるようにします。
Ctrl + F3 を押します。
F9 を押します。