Sie stellen fest, dass Ihr Quelltext in seiner aktuellen Form mehrere Instanzen von LCL_CARRIER zulässt, die dieselbe Fluggesellschaft darstellen. Um dies zu beheben, definieren und implementieren Sie eine Factory-Methode, die sicherstellt, dass nur eine Instanz für einen bestimmten Spediteur-ID-Wert angelegt werden kann.
Aufgabe 1: Vorlage kopieren (optional)
Kopieren Sie die Vorlagenklasse /LRN/CL_S4D401_OOS_INTF_USAGE. Wenn Sie die vorherige Übung abgeschlossen haben, können Sie diese Aufgabe überspringen und mit der Bearbeitung Ihrer Klasse ZCL_##_SOLUTION fortfahren.
Schritte
Kopieren Sie die Klasse /LRN/CL_S4D401_OOS_INTF_USAGE in eine Klasse in Ihrem eigenen Paket (Namensvorschlag: ZCL_##_SOLUTION, wobei ## für Ihre Gruppennummer steht).
Klicken Sie im Project Explorer mit der rechten Maustaste auf die Klasse /LRN/CL_S4D401_OOS_INTF_USAGE, um das Kontextmenü zu öffnen.
Wählen Sie im Kontextmenü Duplizieren....
Geben Sie den Namen Ihres Pakets in das Feld Package ein. Geben Sie im Feld NameZCL_##_SOLUTION ein, wobei ## für Ihre Gruppennummer steht.
Wählen Sie Next.
Bestätigen Sie den Transportauftrag, und wählen Sie Finish.
Aktivieren Sie die Kopie.
Drücken Sie Strg + F3, um die Klasse zu aktivieren.
Aufgabe 2: Factory-Methode definieren
Verwenden Sie einen Quickfix, um eine Factory-Methode für die lokale Klasse LCL_CARRIER zu definieren (Namensvorschlag: GET_INSTANCE). Führen Sie in der Factory-Methode die Existenzprüfung und die Berechtigungsprüfung durch, bevor Sie tatsächlich eine Instanz anlegen. Schränken Sie anschließend die Instanzerzeugung für diese Klasse ein, um die Verwendung der Factory-Methode zu erzwingen.
Schritte
Verwenden Sie in der lokalen Klasse LCL_CARRIER einen Quickfix, um eine Factory-Methode zu definieren.
Hinweis
Die Signatur der Factory-Methode wird standardmäßig um Konstruktorparameter erweitert. Wählen Sie daher das Attribut carrier_id nicht explizit als zu initialisierendes Attribut aus.
Navigieren Sie zur Definition von LCL_CARRIER.
Positionieren Sie in der Anweisung CLASS … DEFINITION den Cursor auf lcl_carrier, und drücken Sie Strg + 1, um die verfügbaren Quickfixes aufzurufen.
Wählen Sie aus der Liste der verfügbaren Quickfixes Generieren Factory-Methode Anlegen.
Geben Sie im Feld NameGET_INSTANCE ein.
Wählen Sie im Abschnitt Zu initialisierende Attribute auswählen die Option Alle Markierungen aufheben und dann Fertigstellen.
Das Quickfix sollte die folgende Methodendefinition hinzugefügt haben:
12345
CLASS-METHODS get_instance
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RETURNING
value(r_result) TYPE REF TO lcl_carrier.
… und die folgende Implementierung:
123
METHOD get_instance.
r_result = NEW #( i_carrier_id = i_carrier_id ).
ENDMETHOD.
Verschieben Sie die SELECT-Anweisung und die AUTHORITY-CHECK-Anweisung aus dem CONSTRUCTOR in die Implementierung der Factory-Methode. Stellen Sie sicher, dass Sie die Prüfungen vor der Instanziierung durchführen.
Navigieren Sie zur Implementierung der Methode CONSTRUCTOR der lokalen Klasse LCL_CARRIER.
Wählen Sie die SELECT-Anweisung und die AUTHORITY-CHECK-Anweisung aus, einschließlich der Auswertung des Systemfelds SY-SUBRC und der auslösenden Ausnahme, und drücken Sie Strg + C, um den Code-Snippet in die Zwischenablage zu kopieren.
Drücken Sie Strg + <, um vor jeder ausgewählten Codezeile ein Kommentarzeichen hinzuzufügen.
Navigieren Sie zur Implementierung der Methode GET_INSTANCE, positionieren Sie den Cursor vor der Wertzuweisung für den Parameter r_result, und drücken Sie Strg + V, um den Code-Snippet einzufügen.
Der Quelltext sollte nun wie folgt aussehen:
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.
Da Sie die RAISE EXCEPTION-Anweisungen aus der Implementierung der Methode CONSTRUCTOR entfernt haben, löschen Sie nun die beiden entsprechenden RAISING-Zusätze in der Signatur der Methodendefinition CONSTRUCTOR.
Navigieren Sie zur Definition der Methode CONSTRUCTOR in LCL_CARRIER.
Passen Sie die Methodendefinition wie folgt an:
123456
METHODS constructor
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
.
Kehren Sie zur Implementierung der Methode GET_INSTANCE zurück. Passen Sie in der SELECT-Anweisung die INTO-Klausel an. Speichern Sie das Ergebnis in einer inline deklarierten Struktur (vorgeschlagener Name: Details) anstelle der Instanzattribute name und currency_code.
Passen Sie den Quelltext wie folgt an:
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).
Ordnen Sie nach der Instanziierung des Trägerobjekts die Werte aus der Detailstruktur den entsprechenden Instanzattributen der neuen Instanz zu.
Passen Sie den Quelltext wie folgt an:
12345
r_result = NEW #( i_carrier_id = i_carrier_id ).
r_result->name = details-name.
r_result->currency_code = details-currency_code.
ENDMETHOD.
Verwenden Sie eine Schnellkorrektur, um auslösende Deklarationen für die beiden Ausnahmen hinzuzufügen.
Wählen Sie das Warnsymbol mit der Glühbirne auf der linken Seite der ersten RAISE EXCEPTION-Anweisung, um die verfügbaren Quickfixes aufzurufen.
Wählen Sie in der Liste der verfügbaren Quickfixes Auslösungsdeklaration hinzufügen.
Wiederholen Sie dies für die andere Anweisung RAISE EXCEPTION.
Die Schnellkorrektur sollte den Quelltext wie folgt anpassen:
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.
Schränken Sie die Instanziierung der lokalen Klasse LCL_CARRIER ein. Stellen Sie sicher, dass die Instanziierung nur innerhalb der Klasse selbst erlaubt ist.
Navigieren Sie zur Definition von LCL_CARRIER.
Passen Sie den Quelltext wie folgt an:
1
CLASS lcl_carrier DEFINITION CREATE PRIVATE.
Verschieben Sie die Definition der Methode CONSTRUCTOR in den privaten Bereich.
Notiz
Dies ist optional, sollte aber zur Verbesserung der Lesbarkeit erfolgen.
Kopieren Sie die METHODS-Anweisung für die Methode CONSTRUCTOR aus dem öffentlichen Bereich, und fügen Sie sie in den privaten Bereich ein.
Ersetzen Sie in der Methode IF_OO_ADT_CLASSRUN~MAIN der globalen Klasse den NEW -Ausdruck durch einen Aufruf der Factory-Methode GET_INSTANCE.
Passen Sie den Quelltext wie folgt an:
12
DATA(carrier) = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
Ersetzen Sie in der Methode CLASS_SETUP der lokalen Testklasse LTCL_FIND_FLIGHTS den Ausdruck NEW durch einen Aufruf der Factory-Methode GET_INSTANCE.
Passen Sie den Quelltext wie folgt an:
12
the_carrier = lcl_carrier=>get_instance( i_carrier_id = some_flight_data-carrier_id ).
Aktivieren und testen Sie Ihre globale Klasse als Konsolen-App. Debuggen Sie die Ausführung der Methode GET_INSTANCE.
Drücken Sie Strg + F3.
Drücken Sie F9.
Aufgabe 3: Factory-Methode erweitern
Definieren Sie in der lokalen Klasse LCL_CARRIER ein statisches Attribut, in dem Referenzen auf die Instanzen von LCL_CARRIER abgelegt werden können (Namensvorschlag: Instanzen). Erweitern Sie anschließend die Implementierung der Factory-Methode: Speichern Sie nach dem Anlegen einer Instanz die Referenz auf die neue Instanz in Attributinstanzen. Legen Sie nur dann eine neue Instanz an, wenn Instanzen noch kein Objekt für die angeforderte Spediteur-ID referenzieren.
Schritte
Verwenden Sie in der lokalen Klasse LCL_CARRIER einen Quickfix, um einen Tabellentyp für diese Klasse anzulegen (Namensvorschlag: tt_carrier).
Navigieren Sie zur Definition der lokalen Klasse LC_CARRIER.
Positionieren Sie in der Anweisung CLASS … DEFINITION den Cursor auf lcl_carrier, und drücken Sie Strg + 1, um die verfügbaren Quickfixes aufzurufen.
Wählen Sie aus der Liste der verfügbaren Quickfixes die Option Tabellentyp für lcl_carrier generieren.
Ersetzen Sie in der neuen TYPES -Anweisung tab durch tt_carrier.
Die TYPES -Anweisung sollte nun wie folgt aussehen:
12
TYPES: tt_carriers TYPE STANDARD TABLE OF REF TO lcl_carrier
WITH DEFAULT KEY.
Definieren Sie ein privates statisches Attribut basierend auf dem soeben angelegten Tabellentyp (Namensvorschlag: Instanzen).
Blättern Sie nach unten zur PRIVATE SECTION -Anweisung.
Passen Sie den Quelltext wie folgt an:
12
PRIVATE SECTION.
CLASS-DATA instances TYPE tt_carriers.
Passen Sie die Implementierung der Factory-Methode GET_INSTANCE an. Fügen Sie nach der Instanziierung dem Instanzenattribut eine Referenz auf die neue Instanz hinzu.
Passen Sie den Quelltext wie folgt an:
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.
Implementieren Sie vor der Instanziierung, aber nach den Existenz- und Berechtigungsprüfungen einen einzelnen Datensatz, der aus Attributinstanzen gelesen wird. Lesen Sie die Instanz, in der der Wert des Attributs carrier_id dem Wert des Importparameters i_carrier_id entspricht. Speichern Sie das Ergebnis im Rückgabeparameter r_result.
Hinweis
Beachten Sie, dass bei internen Tabellen mit nicht strukturiertem Zeilentyp die Daten über die Pseudokomponente table_line angesprochen werden.
Passen Sie den Quelltext wie folgt an:
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.
Umschließen Sie den Einzelsatzlesezugriff, die Instanziierung, die Wertzuweisungen an die Attribute und die APPEND-Anweisung mit einem TRY... ENDTRY Kontrollstruktur.
Passen Sie den Quelltext wie folgt an:
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.
Stellen Sie sicher, dass Sie nur dann eine Instanz anlegen und diese zu Attributinstanzen hinzufügen, wenn die Systemausnahme CX_SY_ITAB_LINE_NOT_FOUND ausgelöst wird.
Passen Sie den Quelltext wie folgt an:
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.
Passen Sie in Ihrer globalen Klasse die Implementierung der Methode IF_OO_ADT_CLASSRUN~MAIN an: Implementieren Sie einen zweiten Aufruf der Factory-Methode GET_INSTANCE mit derselben Eingabe wie der vorhandene Aufruf, aber einer anderen Referenzvariablen für das Ergebnis (Namensvorschlag: carrier2).
Passen Sie den Quelltext wie folgt an:
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 ).
Aktivieren und testen Sie Ihre globale Klasse als Konsolen-App. Debuggen Sie den Quelltext, um sicherzustellen, dass die beiden Aufrufe der Factory-Methode dieselbe Instanz von LCL_CARRIER zurückgeben.
Drücken Sie Strg + F3.
Drücken Sie F9.