Aufgabe

In dieser Aufgabe veerben wir unseren Würfel von der Klasse ZCL_BC_AOO_DIE_HELPER.

Diese Klasse bringt schon einiges für uns mit. Beispielsweise kann diese Klasse auch mit Würfeln umgehen, die mehr oder weniger als 6 Seiten haben und sie kommt auch damit klar, wenn auf den Seiten statt Zahlen Symbole stehen. Also beispielsweise Würfel mit Farben, wo dann nur die gewürfelten Farben gezählt werden sollen.

Unser *Eltern-Würfel' nutzt die Objektorientierung richtig aus, in dem sich jeder Würfel für sich merken kann, wann welche Würfelseite gewürfelt wurde und dann auch eine Statistik anzeigen, wie oft jede Seite gewürfelt wurde.

Das simulierte Spiel geht über 10 Runden mit 5 Würfeln. Dabei hat Würfel 1 4 Seiten und jeder weitere Würfel eine Seite mehr. D.h. Würfel 5 hat dann 8 Seiten.
Hinweis: Bitte ignorieren, dass es einige Kombinationen in der Realität nicht gibt.

Die Aufgabe hat auch eine Redefinition eingebaut. D.h. der neue Würfel soll alles so machen wie das Original. Aber wir wollen die Bedeutung der Seiten ändern. Dazu wird die Methode initialize_faces redefiniert. In einer Bonus-Aufgabe könnte man so über die Anzahl der Seiten unterschiedliche Bedeutungen hinterlegen.

Vorgehensweise

Erbende Klasse anlegen

  • Über den Eclipse Wizard wird eine neue Klasse angelegt
    • Name: ZCL_BC_AOO_##_DIE_INH
    • Description: 'Brandeis Training: ABAP Objects - Würfel - Vererbt ##'
    • Superclass: 'ZCL_BC_AOO_DIE_HELPER'

Danach den Wizard abschließen.

Hinweis:
Im Source ist oben im Abschnitt DEFINITION die Zeile INHERITING FROM zcl_bc_aoo_die_helper. hinzugekommen. Das könnte man auch im Nachgang noch manuell machen, falls man es beim Wizard vergessen hat.

Danach den Code mit Umsch-F1 formatieren und mit Strg-F3 aktivieren.

Hinweis:
Jetzt haben wir ein exaktes Abbild der vererbten Klasse ZCL_BC_AOO_DIE_HELPER. Es ist keine Kopie der Klasse wie man das aus dem klassischen ABAP mit der Kopie von Funktionsgruppen oder Reports gewohnt war. D.h. Änderungen an der vererbten Klasse bleiben erhalten.

Methode vererben

Die Methode INITIALIZE_FACES ist in der vererbten Klasse dafür zuständig den Seiten des Würfels eine Bedeutung zu geben. Wenn man das ändern möchte, dann hilft die Redefinition der entsprechende Methoden.
Welche das sind, erfährt man natürlich aus der gut gepflegten Entwicklungsdokumentation ;-) ... oder durch Debugging.

Folgende Schritte sind dazu notwendig:

  • Ermitteln der Sichtbarkeit der Methode in der Eltern-Klasse (hier 'PROTECTED SECTION')
  • In der gleichen Section wird dann über METHODS <method> REDEFINITION definiert, dass die neue Klasse hier eingreifen möchte
  • Danach kann mit Qickfix die Implementierung erzeugt werden.

In einer solchen redefinierten Implementierung kann durch super->* auf die Implementierung der geerbten Klasse zugegriffen werden. Damit kann man beispielsweise einfache Änderungen nur in Ausnahmefällen implementieren.
In unserem Beispiel soll nur bei Würfeln mit Symbolen eigene Texte für die Seiten erzeugt werden.

Code Snippet Attribute
  METHOD initialize_faces.
    IF iv_face_is_number = abap_true.
      super->initialize_faces(
          iv_face_count = iv_face_count
          iv_face_is_number = iv_face_is_number
      ).
    ELSE.
      CLEAR mt_faces.
      DO iv_face_count TIMES.
        APPEND |MyPage { sy-index }| TO mt_faces.
      ENDDO.
    ENDIF.
  ENDMETHOD.

Mit Pretty-Printer (Umsch-F1) formatieren und aktivieren (Strg-F3)

Test

Zum Testen das persönliche Testtool ZCL_BC_AOO_##_TESTTOOL_BEC mit der Teilnehmernummer aufrufen.
Die Methode für diese Übung heißt: run_inh. Dazu muss die zu testende Methode entweder in die main-Methode mit aufgenommen oder über den Debugger die Variable lv_method auf die entsprechene Methode gesetzt werden.

Die Ausgabe der Console prüfen.
Tipp: Vor dem Start des Tests einmal die Console löschen.

Das Protokoll in der Console ist jetzt deutlich länger. Es enthält die folgenden Informationen:

  • Welcher Würfel mit wie vielen Seiten erzeugt wurde
  • Informationen für alle 10 Runden und welcher Würfel welche Seite gewürfelt hat
  • Je Würfel:
    • eine Zusammenfassung mit Summe und Durchschnitt
    • eine Übersicht, welche Seite wie oft gewürfelt wurde
    • die Reihenfolge, wie diese Seiten gewürfelt wurden

Prüfen Sie das Protokoll.

Lösung

Lösung
CLASS zcl_bc_aoo_##_die_inh DEFINITION
  PUBLIC
  INHERITING FROM zcl_bc_aoo_die_helper
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
  PROTECTED SECTION.
    METHODS initialize_faces REDEFINITION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_bc_aoo_##_die_inh IMPLEMENTATION.
  METHOD initialize_faces.
    IF iv_face_is_number = abap_true.
      super->initialize_faces(
          iv_face_count = iv_face_count
          iv_face_is_number = iv_face_is_number
      ).
    ELSE.
      CLEAR mt_faces.
      DO iv_face_count TIMES.
        APPEND |MyPage { sy-index }| TO mt_faces.
      ENDDO.
    ENDIF.
  ENDMETHOD.

ENDCLASS.

Zusatzaufgaben

Analyse durch Debugging

Nutzen Sie den Debugger, um das Verhalten der Elternklasse zu beobachten. Dazu am besten aus Sicht des Testtools debugen oder in der Elternklasse im Constructor oder interessanten Methoden einen Breakpoint setzen.

Statistik

Die Elternklasse bietet einiges an Statistik-Funktionalität aus Sicht des einzelnen Würfels. Analysieren sie diese. Nutzen Sie dafür das Debugging.

Bonus: Besondere Würfel

Mit dieser Aufgabe hat man die Chance die Seiten der Würfel je nach Seitenanzahl virtuell zu "bekleben". Beispielsweise den 4-seitigen Würfel mit den Grundrechenarten, den 7-seitigen Würfel mit den Wochentagen oder den 8-seitigen Würfel mit Farben.
Seien Sie kreativ!

Anregung:
Schauen Sie sich die Definition der Methode in der Elternklasse an und legen Sie in ihrer Klasse eine eigene Methode initialize_faces_bonus:(Sichtbarkeit protected) an. Dann bleibt die vererbte Methode klein und übersichtlich (Stichwort Wartbarkeit). Sie können dann Ihre Methode analog des super->*-Aufrufs umleiten.

Bonus Lösung
CLASS zcl_bc_aoo_77_die_inh DEFINITION
  PUBLIC
  INHERITING FROM zcl_bc_aoo_die_helper
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
  PROTECTED SECTION.
    METHODS initialize_faces REDEFINITION.

    METHODS initialize_faces_bonus
      IMPORTING
        iv_face_count     TYPE i
        iv_face_is_number TYPE abap_bool DEFAULT abap_true.

  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_bc_aoo_77_die_inh IMPLEMENTATION.

  METHOD initialize_faces.
    IF iv_face_is_number = abap_true.
      super->initialize_faces(
          iv_face_count = iv_face_count
          iv_face_is_number = iv_face_is_number
      ).
    ELSE.
      initialize_faces_bonus(
        iv_face_count = iv_face_count
        iv_face_is_number = iv_face_is_number
      ).
    ENDIF.
  ENDMETHOD.


  METHOD initialize_faces_bonus.
    CLEAR mt_faces.
    CASE iv_face_count.

      WHEN 4.
        APPEND |+| TO mt_faces.
        APPEND |-| TO mt_faces.
        APPEND |*| TO mt_faces.
        APPEND |/| TO mt_faces.
      WHEN 7.
        APPEND |Mo| TO mt_faces.
        APPEND |Di| TO mt_faces.
        APPEND |Mi| TO mt_faces.
        APPEND |Do| TO mt_faces.
        APPEND |Fr| TO mt_faces.
        APPEND |Sa| TO mt_faces.
        APPEND |So| TO mt_faces.
      WHEN 8.
        APPEND |rot| TO mt_faces.
        APPEND |grün| TO mt_faces.
        APPEND |blau| TO mt_faces.
        APPEND |gelb| TO mt_faces.
        APPEND |weiß| TO mt_faces.
        APPEND |schwarz| TO mt_faces.
        APPEND |orange| TO mt_faces.
        APPEND |grau| TO mt_faces.
      WHEN OTHERS.
        DO iv_face_count TIMES.
          APPEND |MyPage { sy-index }| TO mt_faces.
        ENDDO.
    ENDCASE.

  ENDMETHOD.

ENDCLASS.

Weitere Hinweise

  1. Diese Aufgabe hat gezeigt, wie man durch Vererbung und minimalen Aufwand in den Ablauf eingreifen kann. Voraussetzung ist allerdings, dass bei der Definition der Elternklassen bereits mitgedacht wurde und gewisse Sollbruchstellen vorhanden sind.
  2. Im Code run_inh sind einige fortgeschrittene Techniken der dynamischen Programmierung enthalten. Beispiel die Erzeugung von Objekten mit unterschiedlicher Constructor-Signatur wird über eine Factory-Methode mit einem Context_Type gesteuert.
  3. Eine objektorientierte Vererbung fühlt sich an wie eine exakte Kopie ist aber keine!