You realize that, as it is now, your code allows the existence of several instances of LCL_CARRIER that represent the same airline. To amend this, you define and implement an factory method that ensures that only one instance is created for a given carrier ID value.
Task 1: Copy Template (Optional)
Copy the template class. If you finished the previous exercise, you can skip this task and continue editing your class ZCL_##_SOLUTION.
Steps
Copy class /LRN/CL_S4D401_OOS_INTF_USAGE to a class in your own package (suggested name: ZCL_##_SOLUTION, where ## stands for your group number).
In the Project Explorer view, right-click class /LRN/CL_S4D401_OOS_INTF_USAGE to open the context menu.
From the context menu, choose Duplicate ....
Enter the name of your package in the Package field. In the Name field, enter the name ZCL_##_SOLUTION, where ## stands for your group number.
Adjust the description and choose Next.
Confirm the transport request and choose Finish.
Activate the copy.
Press Ctrl + F3 to activate the class.
Task 2: Define a Factory Method
Use a quick fix to define a factory method for local class LCL_CARRIER (suggested name: GET_INSTANCE). In the factory method, perform the existence check and the authority check before you actually create an instance. Then restrict the instance creation for this class to enforce the usage of the factory method.
Steps
In local class LCL_CARRIER, use a quick fix to define a factory method.
Hint
Constructor parameters are added to the signature of the factory method by default. It is not necessary to explicitly select the carrier_id attribute as an attribute to be initialized.Navigate to the definition of LCL_CARRIER.
In the CLASS … DEFINITION statement, place the cursor on lcl_carrier and press Ctrl + 1 to invoke the quick fix.
From the list of available quick fixes, choose Generate factory method create.
In field Name, enter GET_INSTANCE.
In the Select attributes to initialize: section, choose Delesect All, then choose Finish.
The quick fix should have added the following definition:
1234567
CLASS-METHODS get_instance
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RETURNING
value(r_result) TYPE REF TO lcl_carrier.
and the following implementation:
123456789
METHOD get_instance.
r_result = NEW #(
i_carrier_id = i_carrier_id
).
ENDMETHOD.
Move the SELECT statement and the AUTHORITY-CHECK statement from the CONSTRUCTOR to the implementation of the factory method. Make sure your perform the checks before the instantiation.
Navigate to the implementation of the CONSTRUCTOR method of local class LCL_CARRIER.
Select the SELECT statement and the AUTHORITY-CHECK statement, including the evaluation of system field SY-SUBRC and the exception raising, and press Ctrl + C to copy the code snippet to the clipboard.
Press Ctrl + < to add a comment sign in front of each select code row.
Navigate to the implementation of method GET_INSTANCE, place the cursor before the value assignment for parameter r_result and press Ctrl + V to insert the code snippet.
The code should now look like this:
12345678910111213141516171819202122232425262728
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.
In the SELECT statement, adjust the INTO clause. Store the result in an inline declared structure (suggested name: details) instead of instance attributes name and currency_code.
Adjust the code as follows:
123456789
SELECT SINGLE
FROM /lrn/carrier
FIELDS concat_with_space( carrier_id, name, 1 ) AS name,
currency_code
WHERE carrier_id = @i_carrier_id
* INTO ( @me->name, @me->currency_code ).
INTO @DATA(details).
After the instantiation, assign the values from structure details to the instance attributes of the new instance.
Adjust the code as follows:
123456789
r_result = NEW #(
i_carrier_id = i_carrier_id
).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
ENDMETHOD.
Use quick fixes to add raising declarations for the two exceptions.
Choose the warning icon with the light bulb on the left-hand side of either RAISE EXCEPTION statement to invoke the quick fix.
From the list of available quick fixes, choose Add raising declaration.
Repeat for the other RAISING EXCEPTION statement.
The quick fixes should have adjusted the code as follows:
1234567891011
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
.
Restrict the instantiation of local class LCL_CARRIER. Make sure instantiation is only allowed within the class itself.
Navigate to the definition of LCL_CARRIER.
Adjust the code as follows:
123
CLASS lcl_carrier DEFINITION CREATE PRIVATE.
Move the definition of the CONSTRUCTOR method to the private section.
Note
This is optional, but should be done to improve readability.Copy and paste the METHODS statement for method CONSTRUCTOR from the public section to the private section.
In the IF_OO_ADT_CLASSRUN~MAIN method of the global class, replace the NEW expression with a call of the factory method GET_INSTANCE.
Adjust the code as follows:
1234
* DATA(carrier) = NEW lcl_carrier( i_carrier_id = c_carrier_id ).
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
In the CLASS_SETUP method of the local test class LTCL_FIND_FLIGHTS, replace the NEW expression with a call of the factory method GET_INSTANCE.
Adjust the code as follows:
1234
* DATA(carrier) = NEW lcl_carrier( i_carrier_id = c_carrier_id ).
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
Activate and test your global class as console app. Change the value of constant c_carrier_id and debug the execution of the GET_INSTANCE method.
Press Ctrl + F3.
Press F9.
Task 3: Extend the Factory Method
In local class LCL_CARRIER, define a static attribute in which you can store references to the instances of LCL_CARRIER (Suggested name: instances). Extend the implementation of the factory method. After the creation of an instance, store a reference to the new instance in attribute instances. Only create a new instance if instances does not yet contain the reference to an instance for the requested value of carrier ID.
Steps
In local class LCL_CARRIER, use a quick fix to create a table type for this class (suggested name: tt_carriers).
Navigate to the definition of local class LC_CARRIER.
In the CLASS … DEFINITION statement, place the cursor on lcl_carrier and press Ctrl + 1 to invoke the quick fix.
From the list of available quick fixes, choose Generate table type for lcl_carrier.
In the new TYPES statement, replace tab with tt_carriers.
The TYPES statement should now look like this:
1234
TYPES: tt_carriers TYPE STANDARD TABLE OF REF TO lcl_carrier
WITH DEFAULT KEY.
Define a private static attribute based on that table type (suggested name: instances.
Scroll down to the PRIVATE SECTION statement.
Adjust the code as follows:
12345
PRIVATE SECTION.
CLASS-DATA instances TYPE tt_carriers.
Adjust the implementation of the factory method GET_INSTANCE. After the instantiation, add a reference to the new instance to static attribute instances.
Adjust the code as follows:
1234567891011
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.
Before the instantiation, but after the existence and authority checks, implement a single record read from attribute instances. Read the instance in which the value of attribute carrier_id equals the value of import parameter i_carrier_id. Store the result in returning parameter r_result.
Hint
Remember that in internal tables with non-structured row type you address the row using pseudo-component table_line.Adjust the code as follows:
1234567891011121314
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.
Surround the single record read access, the instantiation and the APPEND statement with a TRY... ENDTRY control structure.
Adjust the code as follows:
1234567891011121314
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.
Make sure you only create an instance and add it to attribute instances, if system exception CX_SY_ITAB_LINE_NOT_FOUND is raised.
Adjust the code as follows:
12345678910111213141516
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.
In your local class, adjust the implementation of method IF_OO_ADT_CLASSRUN~MAIN. Duplicate the call of factory method GET_INSTANCE with the same input but a different reference variable for the result (suggested name: carrier2).
Adjust the code as follows:
12345
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
DATA(carrier2) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
Activate and test your global class as console app. Debug the code to ensure that the two calls of the factory method return the same instance of LCL_CARRIER.
Press Ctrl + F3.
Press F9.