From 88e27784f8e6bfb0350bf7e610b82b1c00013a2e Mon Sep 17 00:00:00 2001 From: danrega <16720986+danrega@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:39:07 +0200 Subject: [PATCH] Update --- 04_ABAP_Object_Orientation.md | 685 +++++++++++++++++++++++++++++++++- 06_Dynamic_Programming.md | 62 +-- 2 files changed, 704 insertions(+), 43 deletions(-) diff --git a/04_ABAP_Object_Orientation.md b/04_ABAP_Object_Orientation.md index eb301fc..030faed 100644 --- a/04_ABAP_Object_Orientation.md +++ b/04_ABAP_Object_Orientation.md @@ -115,7 +115,7 @@ You can either create local or global classes: - +
Local classes
  • can be defined within an ABAP program such as in the CCIMP include of global classes (Local Types tab in ADT) or in executable programs ("reports"; in Standard ABAP only)
  • can only be used in the ABAP program in which the class is defined
  • can be defined within an ABAP program such as in include programs of global classes (e.g. the CCIMP include, Local Types table in ADT) or in executable programs ("reports"; in Standard ABAP only)
  • can only be used in the ABAP program in which the class is defined
Global @@ -1549,7 +1549,7 @@ CREATE OBJECT ref4 TYPE some_class. "Corresponds to the result of the expressio ### Working with Reference Variables -This section covers some aspects of working with reference variables. +This section covers some aspects of working with reference variables. Find a copyable example demonstrating the aspects in the next section. **Assigning Reference Variables** @@ -1570,10 +1570,16 @@ reference](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm? is overwritten when a new object is created with a reference variable already pointing to an instance. ``` abap -ref1 = NEW #( ). +DATA ref TYPE REF TO some_class. -"Existing reference is overwritten -ref1 = NEW #( ). +"Creating a new object +"The example assumes there is a mandatory importing parameter for the +"instance constructor. +ref = NEW #( 1 ). + +"Creating another object using the same object reference variable +"The existing reference is overwritten. +ref = NEW #( 1 ). ``` **Retaining object references**: @@ -1608,27 +1614,209 @@ the [object component selector](https://help.sap.com/doc/abapdocu_cp_index_htm/C `->` via a reference variable. - Static attributes: Accessed (if the attributes are visible) using the [class component selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_component_select_glosry.htm "Glossary Entry") -`=>` via the class name. You can also declare data objects and -types by referring to static attributes. +`=>` via the class name. Static attributes can but should not be addressed via a reference variable. + ``` abap "Accessing instance attribute via an object reference variable - ... ref->some_attribute ... "Accessing static attributes via the class name - ... some_class=>static_attribute ... "Without the class name only within the class itself ... static_attribute ... "Type and data object declarations - TYPES some_type LIKE some_class=>some_static_attribute. DATA dobj1 TYPE some_class=>some_type. DATA dobj2 LIKE some_class=>some_static_attribute. ``` +The following executable example addresses aspects of this section and the previous one: + +```abap +CLASS zcl_demo_abap DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + METHODS constructor IMPORTING ts TYPE utclong OPTIONAL. + DATA timestamp TYPE utclong. + DATA instance_number TYPE i. + CLASS-DATA static_number TYPE i. + TYPES chars TYPE c LENGTH 4. + CONSTANTS text TYPE chars VALUE `ABAP`. + PROTECTED SECTION. + PRIVATE SECTION. + CLASS-METHODS test_static_method. +ENDCLASS. + +CLASS zcl_demo_abap IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + + DATA oref TYPE REF TO zcl_demo_abap. + DATA oref2 TYPE REF TO zcl_demo_abap. + +*&---------------------------------------------------------------------* +*& Assigning object reference variables +*&---------------------------------------------------------------------* + + oref = NEW #( utclong_current( ) ). + + "Accessing instance attribute + DATA(ts1) = oref->timestamp. + out->write( data = ts1 name = `ts1` ). + + "Assigning reference to existing reference variable + oref2 = oref. + DATA(ts2) = oref2->timestamp. + out->write( data = ts2 name = `ts2` ). + +*&---------------------------------------------------------------------* +*& Overwriting a reference variable +*&---------------------------------------------------------------------* + + oref = NEW #( utclong_current( ) ). + DATA(ts3) = oref->timestamp. + out->write( data = ts3 name = `ts3` ). + +*&---------------------------------------------------------------------* +*& Retaining object references +*&---------------------------------------------------------------------* + + DATA oref_tab TYPE TABLE OF REF TO zcl_demo_abap. + + DO 3 TIMES. + oref = NEW #( utclong_current( ) ). + oref->instance_number = sy-index. + oref->static_number = sy-index. + oref_tab = VALUE #( BASE oref_tab ( oref ) ). + ENDDO. + + out->write( data = oref_tab name = `oref_tab` ). + +*&---------------------------------------------------------------------* +*& Clearing object references +*&---------------------------------------------------------------------* + + DATA oref_generic TYPE REF TO object. + oref_generic = NEW zcl_demo_abap( utclong_current( ) ). + + IF oref_generic IS INITIAL. + out->write( `oref_generic is initial` ). + ELSE. + out->write( `oref_generic is not initial` ). + ENDIF. + + IF oref_generic IS INSTANCE OF zcl_demo_abap. + out->write( `oref_generic is an instance of zcl_demo_abap` ). + ELSE. + out->write( `oref_generic is not an instance of zcl_demo_abap` ). + ENDIF. + + CLEAR oref_generic. + + IF oref_generic IS INITIAL. + out->write( `oref_generic is initial` ). + ELSE. + out->write( `oref_generic is not initial` ). + ENDIF. + + IF oref_generic IS INSTANCE OF zcl_demo_abap. + out->write( `oref_generic is an instance of zcl_demo_abap` ). + ELSE. + out->write( `oref_generic is not an instance of zcl_demo_abap` ). + ENDIF. + +*&---------------------------------------------------------------------* +*& Accessing class attributes +*&---------------------------------------------------------------------* + + "Accessing instance attribute via an object reference variable + oref = NEW #( utclong_current( ) ). + "Read access + DATA(ts4) = oref->timestamp. + "Write access + oref->instance_number = 123. + + "Accessing static attributes via the class name + "Write access + zcl_demo_abap=>static_number = 456. + "Read access + DATA(static_attr_access) = zcl_demo_abap=>static_number. + "Read access (constant) + DATA(const_value) = zcl_demo_abap=>text. + + "Accessing static attributes within the class without specifying the class name + "See the test_static_method method for restrictions regarding instance attributes. + "Write access + static_number = 789. + "Read access + static_attr_access = static_number. + "Read access (constant) + const_value = text. + + "Type and data object declarations based on class attributes + TYPES type1 TYPE zcl_demo_abap=>chars. + TYPES type2 LIKE zcl_demo_abap=>static_number. + TYPES type3 LIKE zcl_demo_abap=>text. + DATA dobj1 TYPE zcl_demo_abap=>chars. + DATA dobj2 LIKE zcl_demo_abap=>static_number. + DATA dobj3 LIKE zcl_demo_abap=>text. + + "Without class name specification within the class itself + TYPES type4 TYPE chars. + TYPES type5 LIKE static_number. + TYPES type6 LIKE text. + DATA dobj4 TYPE chars. + DATA dobj5 LIKE static_number. + DATA dobj6 LIKE text. + + "Using instance attributes + "Note: You can access static attributes via object reference variables. + "However, it is advisable to access static attributes as shown above. + DATA(oref3) = NEW zcl_demo_abap( utclong_current( ) ). + TYPES type7 TYPE oref3->chars. + TYPES type8 LIKE oref3->instance_number. + + DATA dobj7 TYPE oref3->chars. + DATA dobj8 LIKE oref3->timestamp. + ENDMETHOD. + + METHOD constructor. + timestamp = ts. + ENDMETHOD. + + METHOD test_static_method. + "Accessing static attributes + zcl_demo_abap=>static_number = 987. + "Read access + DATA(static_attr_access) = zcl_demo_abap=>static_number. + "Read access (constant) + DATA(const_value) = zcl_demo_abap=>text. + + "Accessing attributes within the class without specifying the class name + "Write access + static_number = 654. + "Read access + static_attr_access = static_number. + "Read access (constant) + const_value = text. + + "Access to instance attributes in static methods like above is not possible + DATA inst_attr_access TYPE utclong. + "inst_attr_access = timestamp. + "Instance attribute access via object reference variable + DATA(oref) = NEW zcl_demo_abap( utclong_current( ) ). + inst_attr_access = oref->timestamp. + ENDMETHOD. +ENDCLASS. +``` + +

⬆️ back to top

### Calling Methods @@ -1643,6 +1831,7 @@ called without `class_name=>...`. - You might also stumble on method calls with the older [`CALL METHOD`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapcall_method_static.htm) statements. It is recommended to use the new syntax in new developments. Note that `CALL METHOD` statements are still required in the context of [dynamic programming](06_Dynamic_Programming.md). Therefore, `CALL METHOD` statements should be reserved for dynamic method calls. - Find an example class demonstrating various method calls in section [Excursion: Example Class](#excursion-example-class). +- When calling methods that declare importing and/or exporting parameters, keep the following in mind: For methods with importing parameters, you can or must (if other parameters are available and specified in the method call) precede the parameters and their assignments with `EXPORTING`. For methods with exporting parameters, you can or must use `IMPORTING` before the parameters and their assignments. Examples for instance method calls and static method calls: @@ -3616,7 +3805,7 @@ CLASS zcl_demo DEFINITION
-- `... PUBLIC ...`: Specifies that the class is a global class, available globally within the class library. Most of the subsequent snippets use the `PUBLIC` addition as the focus is on global classes. +- `... PUBLIC ...`: Specifies that the class is a global class, available globally within the class library. The cheat sheet also includes example classes that do not specify `PUBLIC`, declaring these classes as local classes. - `... FINAL ...`: Specifies that the class cannot have any subclasses, effectively prohibiting inheritance. This addition seals off a branch of the inheritance tree. In final classes, all methods are automatically final. - `... CREATE PUBLIC ...`: Specifies that the class can be instantiated wherever it is visible. Not specifying the addition `CREATE ...` means `CREATE PUBLIC` by default. @@ -7230,7 +7419,7 @@ SET HANDLER handler3. > **💡 Note**
> - The examples neither represent best practices nor role models. They only aim to experiment with the patterns in simplified contexts and convey the basic concepts. -> - More design patterns exist beyond those covered here, and different implementations or class setup strategies may apply. +> - More design patterns exist beyond those covered here. Different implementations, combinations of patterns and class setup strategies may apply. > - Most examples are structured for easy exploration using simple, self-contained ABAP classes (i.e. only 1 class pool including local classes instead of multiple global classes) as follows: > - Global class: > - Includes the `if_oo_adt_classrun` interface to run the class with F9 in ADT. @@ -10579,6 +10768,478 @@ ENDCLASS. +
+ +
+ 🟢 Facade + + +
+ +- Assume you must handle complex functionality that may involve creating objects from multiple classes and calling methods, maybe even in a specific sequence and involving certain dependencies. +- The facade design pattern's purpose is to provide a simplified API, hiding the complexity from users and allowing them to achieve desired results without dealing with and knowing about underlying complexities. +- Users can interact with this straightforward API instead of handling intricate steps and details. + +Example notes: +- The simplified example uses a travel search context. Users receive information about available flights, hotels, and rental cars. This information is retrieved by creating instances of multiple classes and various method calls. A facade class provides a simplified API that manages complex underlying functionality. Users interact only with the facade class, eliminating the need to handle individual class objects directly. +- The example demonstrates the facade design pattern with the following declarations and implementations: + - Global class: + - Implements the `if_oo_adt_classrun` interface and calls methods from local classes. + - Serves as a vehicle for demonstrating the design pattern. The declarations and implementations in the `CCIMP` are relevant for the for conceptual considerations. + - The global class represents the user of the simplified API the facade class offers. + - CCIMP include (Local Types tab in ADT): + - Contains multiple local classes (`lcl_*`) that provide information about available flights, hotels and rental cars. + - The simple classes include a method that calls a method in another class. Information is retrieved based on origin, destination, and travel time span. + - The `lcl_travel_facade` class serves as the facade class. The `plan_travel` method includes object creations and method calls. For simplicity, the method returns information about available flights, hotels, and rental cars in a string table. +- The class execution involves the following: + - The global class contains several method calls to the facade class. + - Based on the parameters passed, travel options are evaluated. As a result, a string table is output showing travel search information. + + + + + + + + + + + + + + + + + + + +
Class include Code
+ +Global class + + + +``` abap +CLASS zcl_demo_abap DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS zcl_demo_abap IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + DATA(travel_object) = NEW lcl_travel_facade( ). + DATA(travel_options_1) = travel_object->plan_travel( + from = 'Frankfurt' + to = 'Shanghai' + arrival = '20250511' + departure = '20250521' ). + + out->write( travel_options_1 ). + out->write( |\n{ repeat( val = `*` occ = 75 ) }\n\n| ). + + DATA(travel_options_2) = travel_object->plan_travel( + from = 'Frankfurt' + to = 'Shanghai' + arrival = '20250605' + departure = '20250617' ). + + out->write( travel_options_2 ). + out->write( |\n{ repeat( val = `*` occ = 75 ) }\n\n| ). + + DATA(travel_options_3) = travel_object->plan_travel( + from = 'Frankfurt' + to = 'Shanghai' + arrival = '20250403' + departure = '20250410' ). + + out->write( travel_options_3 ). + ENDMETHOD. +ENDCLASS. +``` + +
+ +CCIMP include (Local Types tab in ADT) + + + +``` abap +"Class that includes a method to get flights from a data source +"In this simple, self-contained example, the data source is +"simulated by an internal table that includes demo data. +CLASS lcl_flight_retrieval DEFINITION. + PUBLIC SECTION. + TYPES: BEGIN OF flight_struc, + from TYPE c LENGTH 20, + to TYPE c LENGTH 20, + flight_date TYPE d, + flight_time TYPE t, + END OF flight_struc, + flight_tab_type TYPE TABLE OF flight_struc WITH EMPTY KEY. + + METHODS get_flights + IMPORTING + from TYPE string + to TYPE string + flight_date TYPE d + RETURNING + VALUE(flights) TYPE flight_tab_type. + CLASS-METHODS class_constructor. + PRIVATE SECTION. + CLASS-DATA flight_tab TYPE flight_tab_type. +ENDCLASS. + +CLASS lcl_flight_retrieval IMPLEMENTATION. + METHOD get_flights. + SELECT * FROM @flight_tab AS tab + WHERE from = @from AND to = @to AND flight_date = @flight_date + INTO TABLE @flights. + ENDMETHOD. + METHOD class_constructor. + flight_tab = VALUE #( + ( from = 'Frankfurt' to = 'Shanghai' flight_date = '20250511' flight_time = '050000' ) + ( from = 'Frankfurt' to = 'Shanghai' flight_date = '20250511' flight_time = '200000' ) + ( from = 'Frankfurt' to = 'Shanghai' flight_date = '20250605' flight_time = '151500' ) + ( from = 'Frankfurt' to = 'Shanghai' flight_date = '20250725' flight_time = '070000' ) + ( from = 'Shanghai' to = 'Frankfurt' flight_date = '20250410' flight_time = '194500' ) + ( from = 'Shanghai' to = 'Frankfurt' flight_date = '20250521' flight_time = '123000' ) + ( from = 'Shanghai' to = 'Frankfurt' flight_date = '20250805' flight_time = '184500' ) ). + ENDMETHOD. +ENDCLASS. + +"An object of the lcl_flight_search class is required for searching flights. This object is +"created by the facade class. The lcl_flight_search class implementation includes the calling +"of a method of the lcl_flight_retrieval class to retrieve a list of available flights. +CLASS lcl_flight_search DEFINITION. + PUBLIC SECTION. + METHODS search_flights + IMPORTING + from TYPE string + to TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(flight_list) TYPE string_table. +ENDCLASS. + +CLASS lcl_flight_search IMPLEMENTATION. + METHOD search_flights. + DATA(flight_obj) = NEW lcl_flight_retrieval( ). + DATA(result) = flight_obj->get_flights( + from = from + to = to + flight_date = arrival ). + + IF result IS INITIAL. + APPEND |X \| There's no flight from { from } to { to } available on { arrival }.| TO flight_list. + ELSE. + LOOP AT result INTO DATA(flight_wa). + APPEND |OK \| Flight from { flight_wa-from } to { flight_wa-to } available on { flight_wa-flight_date } at { flight_wa-flight_time }.| TO flight_list. + ENDLOOP. + ENDIF. + + result = flight_obj->get_flights( + from = to + to = from + flight_date = departure ). + + IF result IS INITIAL. + APPEND |X \| There's no flight from { to } to { from } available on { departure }.| TO flight_list. + ELSE. + LOOP AT result INTO flight_wa. + APPEND |OK \| Flight from { flight_wa-from } to { flight_wa-to } available on { flight_wa-flight_date } at { flight_wa-flight_time }.| TO flight_list. + ENDLOOP. + ENDIF. + ENDMETHOD. +ENDCLASS. + +********************************************************************** + +"Class to get hotels from a data source +"In this simple, self-contained example, the data source is +"simulated by an internal table that includes demo data with +"hotel details such as unavailable dates. +CLASS lcl_hotel_retrieval DEFINITION. + PUBLIC SECTION. + TYPES: BEGIN OF hotel_struc, + city TYPE c LENGTH 20, + hotel_name TYPE c LENGTH 20, + is_available TYPE abap_boolean, + END OF hotel_struc, + hotel_tab_type TYPE TABLE OF hotel_struc WITH EMPTY KEY. + + METHODS get_hotels + IMPORTING + city TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(hotels) TYPE hotel_tab_type. + CLASS-METHODS class_constructor. + PRIVATE SECTION. + CLASS-DATA hotel_tab TYPE hotel_tab_type. + TYPES: BEGIN OF hotel_struc_availability, + city TYPE c LENGTH 20, + hotel_name TYPE c LENGTH 20, + unavailable_date TYPE d, + END OF hotel_struc_availability, + hotel_tab_availability_type TYPE TABLE OF hotel_struc_availability WITH EMPTY KEY. + CLASS-DATA hotel_tab_availability TYPE hotel_tab_availability_type. + DATA date_tab TYPE TABLE OF d WITH EMPTY KEY. + DATA flag TYPE abap_boolean. +ENDCLASS. + +CLASS lcl_hotel_retrieval IMPLEMENTATION. + METHOD get_hotels. + SELECT * FROM @hotel_tab_availability AS tab + WHERE city = @city + INTO TABLE @DATA(hotel_list). + + LOOP AT hotel_list INTO DATA(waf) GROUP BY ( key = waf-hotel_name ) ASCENDING INTO DATA(keyf). + LOOP AT GROUP keyf INTO DATA(memberf). + APPEND memberf-unavailable_date TO date_tab. + ENDLOOP. + + "Checking whether hotels are available during the travel time span + LOOP AT date_tab INTO DATA(date). + IF date >= arrival AND date <= departure. + flag = abap_false. + EXIT. + ELSE. + flag = abap_true. + ENDIF. + ENDLOOP. + + IF flag = abap_true. + APPEND VALUE #( city = memberf-city hotel_name = memberf-hotel_name is_available = abap_true ) TO hotels. + ENDIF. + + CLEAR flag. + CLEAR date_tab. + ENDLOOP. + + ENDMETHOD. + METHOD class_constructor. + hotel_tab_availability = VALUE #( + ( city = 'Frankfurt' hotel_name = 'ABC' unavailable_date = '20250512' ) + ( city = 'Frankfurt' hotel_name = 'ABC' unavailable_date = '20250612' ) + ( city = 'Frankfurt' hotel_name = 'DEF' unavailable_date = '20250512' ) + ( city = 'Frankfurt' hotel_name = 'DEF' unavailable_date = '20250712' ) + ( city = 'Frankfurt' hotel_name = 'GHI' unavailable_date = '20250512' ) + ( city = 'Frankfurt' hotel_name = 'GHI' unavailable_date = '20250812' ) + ( city = 'Shanghai' hotel_name = 'JKL' unavailable_date = '20250512' ) + ( city = 'Shanghai' hotel_name = 'JKL' unavailable_date = '20250610' ) + ( city = 'Shanghai' hotel_name = 'MNO' unavailable_date = '20250712' ) + ( city = 'Shanghai' hotel_name = 'MNO' unavailable_date = '20250611' ) + ( city = 'Shanghai' hotel_name = 'PQR' unavailable_date = '20250408' ) + ( city = 'Shanghai' hotel_name = 'PQR' unavailable_date = '20250912' ) + ( city = 'Shanghai' hotel_name = 'PQR' unavailable_date = '20250612' ) ). + ENDMETHOD. +ENDCLASS. + +"An object of the lcl_hotel_search class is required for searching hotels. This object is +"created by the facade class. The lcl_hotel_search class implementation includes the calling +"of a method of the lcl_hotel_retrieval class to retrieve a list of available hotels. +CLASS lcl_hotel_search DEFINITION. + PUBLIC SECTION. + METHODS search_hotel + IMPORTING + destination TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(hotel_list) TYPE string_table. +ENDCLASS. + +CLASS lcl_hotel_search IMPLEMENTATION. + METHOD search_hotel. + + DATA(hotel_obj) = NEW lcl_hotel_retrieval( ). + DATA(result) = hotel_obj->get_hotels( + city = destination + arrival = arrival + departure = departure ). + + IF result IS INITIAL. + APPEND |X \| There's no hotel available in { destination } during your trip from { arrival } to { departure }.| TO hotel_list. + ELSE. + LOOP AT result INTO DATA(hotel_wa). + APPEND |OK \| Hotel "{ hotel_wa-hotel_name }" in { destination } is availble during your trip from { arrival } to { departure }.| TO hotel_list. + ENDLOOP. + ENDIF. + ENDMETHOD. +ENDCLASS. + +********************************************************************** + +"Class to get rental cars from a data source +"In this simple, self-contained example, the data source is +"simulated by an internal table that includes demo data with +"rental car details such as unavailable dates. +CLASS lcl_rental_car_retrieval DEFINITION. + PUBLIC SECTION. + TYPES: BEGIN OF rental_car_struc, + city TYPE c LENGTH 20, + car TYPE c LENGTH 20, + is_available TYPE abap_boolean, + END OF rental_car_struc, + rental_car_tab_type TYPE TABLE OF rental_car_struc WITH EMPTY KEY. + + METHODS get_rental_cars + IMPORTING + city TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(rental_cars) TYPE rental_car_tab_type. + CLASS-METHODS class_constructor. + PRIVATE SECTION. + CLASS-DATA rental_car_tab TYPE rental_car_tab_type. + TYPES: BEGIN OF rental_car_struc_availability, + city TYPE c LENGTH 20, + car TYPE c LENGTH 20, + unavailable_date TYPE d, + END OF rental_car_struc_availability, + rent_car_tab_availability_type TYPE TABLE OF rental_car_struc_availability WITH EMPTY KEY. + CLASS-DATA rental_car_availability_tab TYPE rent_car_tab_availability_type. + DATA date_tab TYPE TABLE OF d WITH EMPTY KEY. + DATA flag TYPE abap_boolean. +ENDCLASS. + +CLASS lcl_rental_car_retrieval IMPLEMENTATION. + METHOD get_rental_cars. + SELECT * FROM @rental_car_availability_tab AS tab + WHERE city = @city + INTO TABLE @DATA(rental_car_list). + + LOOP AT rental_car_list INTO DATA(waf) GROUP BY ( key = waf-car ) ASCENDING INTO DATA(keyf). + LOOP AT GROUP keyf INTO DATA(memberf). + APPEND memberf-unavailable_date TO date_tab. + ENDLOOP. + + LOOP AT date_tab INTO DATA(date). + IF date >= arrival AND date <= departure. + flag = abap_false. + EXIT. + ELSE. + flag = abap_true. + ENDIF. + ENDLOOP. + + IF flag = abap_true. + APPEND VALUE #( city = memberf-city car = memberf-car is_available = abap_true ) TO rental_cars. + ENDIF. + + CLEAR flag. + CLEAR date_tab. + ENDLOOP. + ENDMETHOD. + METHOD class_constructor. + rental_car_availability_tab = VALUE #( + ( city = 'Frankfurt' car = 'Car 1' unavailable_date = '20250512' ) + ( city = 'Frankfurt' car = 'Car 1' unavailable_date = '20250612' ) + ( city = 'Frankfurt' car = 'Car 2' unavailable_date = '20250512' ) + ( city = 'Frankfurt' car = 'Car 2' unavailable_date = '20250712' ) + ( city = 'Frankfurt' car = 'Car 3' unavailable_date = '20250512' ) + ( city = 'Frankfurt' car = 'Car 3' unavailable_date = '20250812' ) + ( city = 'Shanghai' car = 'Car 4' unavailable_date = '20250512' ) + ( city = 'Shanghai' car = 'Car 4' unavailable_date = '20250612' ) + ( city = 'Shanghai' car = 'Car 5' unavailable_date = '20250607' ) + ( city = 'Shanghai' car = 'Car 5' unavailable_date = '20250712' ) + ( city = 'Shanghai' car = 'Car 5' unavailable_date = '20250812' ) + ( city = 'Shanghai' car = 'Car 6' unavailable_date = '20250912' ) + ( city = 'Shanghai' car = 'Car 6' unavailable_date = '20251012' ) ). + ENDMETHOD. +ENDCLASS. + +"An object of the lcl_rental_car_search class is required for searching rental cars. This object is +"created by the facade class. The lcl_rental_car_search class implementation includes the calling +"of a method of the lcl_rental_car_retrieval class to retrieve a list of available rental cars. +CLASS lcl_rental_car_search DEFINITION. + PUBLIC SECTION. + METHODS search_rental_car + IMPORTING + destination TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(rental_car_list) TYPE string_table. +ENDCLASS. + +CLASS lcl_rental_car_search IMPLEMENTATION. + METHOD search_rental_car. + DATA(rental_car_obj) = NEW lcl_rental_car_retrieval( ). + DATA(res) = rental_car_obj->get_rental_cars( + city = destination + arrival = arrival + departure = departure ). + + IF res IS INITIAL. + APPEND |X \| There's no rental car available in { destination } during your trip from { arrival } to { departure }.| TO rental_car_list. + ELSE. + LOOP AT res INTO DATA(rental_car_wa). + APPEND |OK \| "{ rental_car_wa-car }" is available as rental car in { destination } during your trip from { arrival } to { departure }.| TO rental_car_list. + ENDLOOP. + ENDIF. + ENDMETHOD. +ENDCLASS. + +********************************************************************** + +"Facade class that represents a simplified API, hiding the complexity from users and allowing +"them to achieve desired results without dealing with and knowing about underlying complexities. +"It includes various object creations and method calls. +CLASS lcl_travel_facade DEFINITION. + PUBLIC SECTION. + METHODS plan_travel + IMPORTING + from TYPE string + to TYPE string + arrival TYPE d + departure TYPE d + RETURNING + VALUE(reservation_options) TYPE string_table. +ENDCLASS. + +CLASS lcl_travel_facade IMPLEMENTATION. + METHOD plan_travel. + "Abort proceeding with the example when the departure is before the arrival. + IF departure <= arrival. + ASSERT 1 = 0. + ENDIF. + + "Creating multipled objects + DATA(flight_search) = NEW lcl_flight_search( ). + DATA(hotel_search) = NEW lcl_hotel_search( ). + DATA(rental_car_search) = NEW lcl_rental_car_search( ). + + "Adding travel search information to a string table for demonstration and output purposes + APPEND |Travel options for: { from } - { to }, { arrival } - { departure }| TO reservation_options. + DATA(flights) = flight_search->search_flights( from = from to = to arrival = arrival departure = departure ). + APPEND `---------- FLIGHTS -----------------------------------------------------------------------------` TO reservation_options. + APPEND LINES OF flights TO reservation_options. + DATA(hotels) = hotel_search->search_hotel( destination = to arrival = arrival departure = departure ). + APPEND `---------- HOTELS ------------------------------------------------------------------------------` TO reservation_options. + APPEND LINES OF hotels TO reservation_options. + DATA(rental_cars) = rental_car_search->search_rental_car( destination = to arrival = arrival departure = departure ). + APPEND `---------- RENTAL CARS -------------------------------------------------------------------------` TO reservation_options. + APPEND LINES OF rental_cars TO reservation_options. + ENDMETHOD. +ENDCLASS. +``` + +
+ +
+ +

⬆️ back to top

diff --git a/06_Dynamic_Programming.md b/06_Dynamic_Programming.md index eb46ad7..2d47cbe 100644 --- a/06_Dynamic_Programming.md +++ b/06_Dynamic_Programming.md @@ -2184,11 +2184,19 @@ DELETE FROM ('ZDEMO_ABAP_CARR') WHERE (where_cl). **Excursion**: To take up the use case mentioned in the introduction about retrieving the content of a database table, storing it in an internal table, and displaying it when the database table name is specified dynamically at -runtime, see the following code snippet. Note the comments. +runtime, see the following code snippet. + +> **💡 Note**
+> - The example retrieves the content of a database table, storing it in an internal table, and displaying it when the database table name is specified dynamically at runtime. +> - For demo purposes in test programs, there are quite some ways to achieve it, and that work out of the box. For example, in ABAP for Cloud Development, you can implement the classrun interface `if_oo_adt_classrun` and output content to the ADT console using the `out->write(...).` method. You can also inherit from `cl_demo_classrun` in your class. In classic ABAP, you can, for example and additionally, use `cl_demo_output`. +> - The following example is just ABAP code exploring dynamic programming aspects. Note the [Disclaimer](./README.md#%EF%B8%8F-disclaimer) in the *README* of the cheat sheet repository. It is an example that sets its focus on a dynamic `SELECT` statement and processing internal table content by dynamically accessing structure components. +> - For simplicity, column contents are converted to string here if necessary, i.e. all column contents must be convertible to string. Using the ways mentioned above are way more powerful. For example, in most cases also nested and deep data objects can be displayed for demo purposes. +> - To visualize the result, the snippet uses the classrun methods to display results sequentially - instead of displaying the internal table content retrieved by the `SELECT` statement directly. +> - The example uses database tables from the cheat sheet repository. To fill them, you can use the method call `zcl_demo_abap_aux=>fill_dbtabs( ).`. ```abap -CLASS zcl_example_class DEFINITION +CLASS zcl_demo_abap DEFINITION PUBLIC FINAL CREATE PUBLIC . @@ -2198,32 +2206,8 @@ CLASS zcl_example_class DEFINITION PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. -CLASS zcl_example_class IMPLEMENTATION. +CLASS zcl_demo_abap IMPLEMENTATION. METHOD if_oo_adt_classrun~main. - "The example retrieves the content of a database table, storing it in an - "internal table, and displaying it when the database table name is - "specified dynamically at runtime. - "Certainly, there are quite some ways to achieve it, and that work out - "of the box. For example, in ABAP for Cloud Development, you can implement - "the classrun if_oo_adt_classrun and output content using the out->write(...) - "method. You can also inherit from cl_demo_classrun in your class. In - "classic ABAP, you can, for example and additionally, use cl_demo_output or - "ALV. - "Notes: - "- The following example is just ABAP code exploring dynamic programming - " aspects. Note the disclaimer in the README of the cheat sheet repository. - " It is an example that sets its focus on a dynamic SELECT statement and - " processing internal table content by dynamically accessing structure - " components. - "- The ways mentioned above are way more powerful (e.g. in most cases also - " nested and deep data objects can be displayed for demo purposes). - "- For simplicity, column contents are converted to string here if necessary, - " i.e. all column contents must be convertible to string. - "- For display purposes, the snippet uses the classrun methods to display - " results sequentially - instead of displaying the internal table - " content retrieved by the SELECT statement directly. - "- The example uses database tables from the cheat sheet repository. To fill - " them, you can use the method call zcl_demo_abap_aux=>fill_dbtabs( ).. zcl_demo_abap_aux=>fill_dbtabs( ). "Data objects and types relevant for the example (length and offset for @@ -2281,8 +2265,8 @@ CLASS zcl_example_class IMPLEMENTATION. ASSIGN it_comps[ name = -name ] TO FIELD-SYMBOL(). DATA(max_content) = REDUCE i( INIT len = -len FOR IN itab->* - NEXT len = COND #( WHEN strlen( CONV string( -(-name) ) ) > len - THEN strlen( CONV string( -(-name) ) ) + NEXT len = COND #( LET lv = |{ -(-name) }| IN + WHEN strlen( lv ) > len THEN strlen( lv ) ELSE len ) ). "Extend the length value to leave some more space IF max_content > -len. @@ -4957,7 +4941,7 @@ TYPES: BEGIN OF struc_type_2, END OF struc_type_2. DATA itab_4 TYPE SORTED TABLE OF struc_type_2 WITH NON-UNIQUE KEY e - WITH NON-UNIQUE SORTED KEY secondary_key COMPONENTS f. + WITH NON-UNIQUE SORTED KEY sec_key COMPONENTS f. "Note that the default name primary_key does not need to be specified explicitly. "So, the following declaration corresponds to the previous one. DATA itab_4b TYPE SORTED TABLE OF struc_type_2 @@ -4983,7 +4967,7 @@ DATA(tdo_tab_4) = cl_abap_tabledescr=>get_with_keys( is_unique = abap_false key_kind = cl_abap_tabledescr=>keydefkind_user components = VALUE #( ( name = 'E' ) ) ) - ( name = 'SECONDARY_KEY' + ( name = 'SEC_KEY' is_primary = abap_false access_kind = cl_abap_tabledescr=>tablekind_sorted is_unique = abap_false @@ -5271,6 +5255,22 @@ DATA(tdo_ref_get_ref_to_data) = cl_abap_refdescr=>get_ref_to_data( ). "---- 'get_ref_to_object' method ---- "To get the type description object for the type REF TO OBJECT DATA(tdo_ref_get_ref_to_object) = cl_abap_refdescr=>get_ref_to_object( ). + +"---- Note ---- +"Note the 'get_referenced_type' method when casting to cl_abap_refdescr. +TYPES test_type TYPE c LENGTH 10. +DATA test_dobj TYPE c LENGTH 10. +DATA dref_dobj TYPE REF TO test_type. +dref_dobj = NEW #( ). + +DATA(tdo_ref_get_elemdescr) = CAST cl_abap_refdescr( cl_abap_typedescr=>describe_by_data( dref_dobj ) ). +DATA(tdo_ref_get_referenced_type) = tdo_ref_get_elemdescr->get_referenced_type( ). +DATA(cast_tdo_ref_get_ref_type) = CAST cl_abap_datadescr( tdo_ref_get_referenced_type ). +CREATE DATA dref_ref TYPE HANDLE cast_tdo_ref_get_ref_type. + +DATA(tdo4applies) = CAST cl_abap_elemdescr( cl_abap_typedescr=>describe_by_data( test_dobj ) ). +DATA(applies_to_dobj) = tdo4applies->applies_to_data( dref_ref->* ). +ASSERT applies_to_dobj = abap_true. ```

⬆️ back to top