Analyse et test du code
Utilisation correcte des types de données et des conversions de types
Traitement des zones de caractères
Utilisation du Pushdown de code dans ABAP SQL
Amélioration de la performance des tables internes
Implémentation des contrôles des autorisations
Conception d'un code orienté objet efficace
Définition et utilisation des classes d'exception
Ajout de documentation au code ABAP

Utilisation de l'héritage

Objective

After completing this lesson, you will be able to utilisez l'héritage.

Basculer vers le haut et vers le bas

Pour instancier une sous-classe, vous déclarez une variable de référence avec le type de la classe pertinente. Vous utilisez ensuite l'opérateur NEW pour créer l'instance. Vous devez transmettre des valeurs à tous les paramètres obligatoires du constructeur.

Utilisation de références de surclasse

Une variable de référence avec le type d'une surclasse (dans ce cas lcl_plane) peut également contenir des références à des objets du type d'une sous-classe. Cela est logique, car un avion de passagers ou un avion cargo est un avion.

Cependant, une variable de référence du type de la surclasse ne connaît que les composants définis dans la surclasse. Par conséquent, lorsque vous utilisez une référence de ce type pour gérer une instance d'une sous-classe, vous pouvez uniquement adresser les composants de la surclasse, même si l'objet réel auquel la référence pointe est une instance de la sous-classe.

L'affectation de références d'objet à une variable de référence d'un type différent est appelée transtypage. L'affectation d'une référence qui pointe vers une sous-classe à une variable de référence avec le type d'une surclasse est appelée transtypage ascendant, car vous effectuez l'affectation à un type plus élevé dans la hiérarchie d'héritage.

Appel de méthodes après un Up-Cast

Lorsque vous utilisez une variable de référence de type ref to lcl_plane pour gérer une instance d'avion passager, vous ne pouvez pas l'utiliser pour accéder aux composants qui appartiennent à la classe avion passager, car la classe avion ne sait pas qu'ils existent et le contrôle syntaxique ne peut pas savoir vers quel type d'objet la référence pointera réellement au moment de l'exécution.

A l'aide de la référence, vous pouvez cependant accéder à des méthodes dont la définition d'origine figure dans la classe. C'est le cas de la méthode get_attributes dans l'exemple de la figure. La question est de savoir quelle implémentation de la méthode sera appelée. Dans ce cas, le système recherche au moment de l'exécution le type d'objet vers lequel pointe la variable de référence et appelle l'implémentation à partir de la classe correspondante. En d'autres termes, bien que nous utilisions une référence plan pour gérer une instance de la classe avion passager, c'est toujours la mise en oeuvre du procédé à partir de la classe avion passager qui est appelée.

Affectation d'une référence générique à un type spécifique

Vous pouvez toujours affecter une variable de référence avec le type d'une sous-classe à une variable de référence avec le type d'une surclasse. Par exemple, vous pouvez toujours affecter une référence d'avion passager à une référence d'avion. Comme nous l'avons vu, c'est parce qu'un avion de passagers est un avion. Cependant, l'affectation de variables de référence dans la direction opposée est plus délicate. En effet, une référence de plan peut contenir des références à d'autres classes que la classe d'avion de passagers. En d'autres termes, lorsque chaque avion de passagers est un avion, chaque avion n'est pas un avion de passagers.

Étant donné que l'objet fait référence à un avion de type réf. à lcl_plane et passager_plane de type ref à lcl_passager_plane, le système vous permettra toujours d'écrire avion = passagerer_plane. Cependant, l'affectation passager_plane = avion entraîne une erreur de syntaxe car le contrôle syntaxique ne peut pas être suffisamment sûr que la référence du plan contienne effectivement une référence à un objet d'avion passager à ce moment-là.

L'opérateur CAST

Le contrôle syntaxique ne vous permet pas d'affecter directement une référence de surclasse à une référence de sous-classe. Cependant, vous pouvez toujours le faire à l'aide de l'opérateur CAST. Casting signifie regarder un objet comme s'il avait un type différent. Dans l'exemple, nous disons au système de traiter "avion" comme s'il s'agissait d'une instance de la classe avion passager. C'est syntaxiquement correct, car nous faisons maintenant semblant que "plan" sur le côté droit de l'expression a le même type que "passager" sur le côté gauche. Cependant, il n'y a toujours aucune garantie que l'avion contienne effectivement une référence d'avion passager, et s'il s'avère qu'il n'en contient pas au moment de l'exécution, l'affectation ne peut pas être effectuée. Lue non protégée, cette affectation peut entraîner une erreur d'exécution.

Sécurisation d'un Down-Cast

Si vous n'êtes pas certain que l'objet vers lequel pointe une variable de référence est effectivement une instance de la sous-classe correcte, vous pouvez le vérifier à l'aide de l'expression logique IS INSTANCE OF. Dans cet exemple, l'affectation à la référence de l'avion passager n'est traitée que si l'avion pointe réellement vers une instance de la classe lcl_passager_plane.

Essayer: Up-Cast et Down-Cast

  1. Créez une nouvelle classe qui implémente l'interface IF_OO_ADT_CLASSRUN.
  2. Passez à l'onglet Types locaux et copiez l'extrait de code suivant dans l'éditeur :
    Code Snippet
    123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
    CLASS lcl_plane DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ts_attributes, name TYPE string, value TYPE string, END OF ts_attributes, * declare table - do not allow the same attribute to be used more than once tt_attributes TYPE SORTED TABLE OF ts_attributes WITH UNIQUE KEY name. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string. METHODS: get_Attributes RETURNING VALUE(rt_Attributes) TYPE tt_attributes. PROTECTED SECTION. DATA manufacturer TYPE string. DATA type TYPE string. PRIVATE SECTION. ENDCLASS. CLASS lcl_plane IMPLEMENTATION. METHOD constructor. manufacturer = iv_manufacturer. type = iv_type. ENDMETHOD. METHOD get_attributes. rt_attributes = VALUE #( ( name = 'MANUFACTURER' value = manufacturer ) ( name = 'TYPE' value = type ) ) . ENDMETHOD. ENDCLASS. CLASS lcl_cargo_plane DEFINITION INHERITING FROM lcl_plane. PUBLIC SECTION. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string iv_cargo TYPE i. METHODS get_attributes REDEFINITION. private section. data cargo type i. ENDCLASS. CLASS lcl_cargo_plane IMPLEMENTATION. METHOD constructor. super->constructor( iv_manufacturer = iv_manufacturer iv_type = iv_type ). cargo = iv_cargo. ENDMETHOD. METHOD get_attributes. * method uses protected attributes of superclass rt_attributes = value #( ( name = 'MANUFACTURER' value = manufacturer ) ( name = 'TYPE' value = type ) ( name ='CARGO' value = cargo ) ). ENDMETHOD. ENDCLASS. CLASS lcl_passenger_plane DEFINITION INHERITING FROM lcl_Plane. PUBLIC SECTION. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string iv_seats TYPE i. METHODS get_Attributes REDEFINITION. private section. data seats type i. ENDCLASS. CLASS lcl_passenger_plane IMPLEMENTATION. METHOD constructor. super->constructor( iv_manufacturer = iv_manufacturer iv_type = iv_type ). ENDMETHOD. METHOD get_attributes. * Redefinition uses call of superclass implementation rt_attributes = super->get_attributes( ). rt_Attributes = value #( base rt_attributes ( name = 'SEATS' value = seats ) ). ENDMETHOD. ENDCLASS.
  3. Passez à l'onglet Classe globale et collez l'extrait de code suivant dans l'implémentation de la méthode if_oo_adt_classrun~main( ) :
    Code Snippet
    12345678910111213141516171819202122232425262728293031323334353637383940414243444546
    DATA passenger TYPE REF TO lcl_passenger_Plane. DATA cargo TYPE REF TO lcl_cargo_plane. DATA plane TYPE REF TO lcl_Plane. passenger = NEW #( iv_manufacturer = 'BOEING' iv_type = '737-800' iv_seats = 130 ) . cargo = NEW #( iv_manufacturer = 'AIRBUS' iv_type = 'A340' iv_cargo = 60000 ). out->write( 'Output using passenger plane object reference' ). out->write( passenger->Get_attributes( ) ). plane = passenger. out->write( 'Output using superclass object reference' ). out->write( plane->get_attributes( ) ). * Can't use the superclass reference to get the number of seats of the * passenger plane, because the relevent method isn't declared in the superclass. * plane->get_seats( ). * Can't assign the plane directly to a passenger plane reference variable, because * not every plane is a passenger plane. *passenger = plane. * Make sure the plane is actually a passenger plane, then force the cast. IF plane IS INSTANCE OF lcl_passenger_plane. passenger = CAST #( plane ). ENDIF. ENDMETHOD.
  4. Sélectionnez CTRL + F3 pour activer la classe et F9 pour l'exécuter en tant qu'application de console.
  5. Jouez avec le code source pour vous familiariser avec les concepts.

Travailler avec des références de surclasse

Dans l'exercice précédent, vous avez créé une surclasse commune LCL_FLIGHT pour les classes LCL_PASSENGER_FLIGHT et LCL_CARGO_FLIGHT. Vous allez maintenant modifier la classe locale LCL_CARRIER pour utiliser la relation de transmission de données. Vous saisirez les variables de référence utilisées comme paramètres pour le type de la surclasse et, au lieu de deux attributs d'instance distincts passager_flights et cargo_flights, définissez un attribut d'instance unique vols dans lequel vous pouvez stocker les références à toutes les instances de vol, qu'il s'agisse de vols de passagers ou de fret.

Modèle :

  • /LRN/CL_S4D401_OOS_INHERITANCE (classe globale)

Solution :

  • /LRN/CL_S4D401_OOS_INH_USAGE (classe globale)

Tâche 1: Copier modèle (facultatif)

Copiez la classe de modèle /LRN/CL_S4D401_OOS_INHERITANCE. Si vous avez terminé l'exercice précédent, vous pouvez ignorer cette tâche et continuer à modifier votre classe ZCL_##_SOLUTION.

Étapes

  1. Copiez la classe /LRN/CL_S4D401_OOS_INHERITANCE dans une classe de votre propre package (nom proposé : ZCL_##_SOLUTION, ## correspondant à votre numéro de groupe).

    1. Dans l'explorateur de projets, cliquez avec le bouton droit de la souris sur la classe /LRN/CL_S4D401_OOS_INHERITANCE pour ouvrir le menu contextuel.

    2. Dans le menu contextuel, sélectionnez Dupliquer....

    3. Saisissez le nom de votre package dans la zone Package. Dans la zone Nom, saisissez ZCL_##_SOLUTION, où ## représente votre numéro de groupe.

    4. Cliquez sur Next.

    5. Confirmez l'ordre de transport et cliquez sur Terminer.

  2. Activez la copie.

    1. Appuyez sur Ctrl + F3 pour activer la classe.

Tâche 2: Paramètre générique

Dans la classe locale LCL_CARRIER, adaptez les définitions des méthodes FIND_PASSENGER_FLIGHT et FIND_CARGO_FLIGHT. Dans les deux méthodes, saisissez le paramètre d'exportation e_flight comme variable de référence basée sur la classe locale LCL_FLIGHT.

Étapes

  1. Dans la classe locale LCL_CARRIER, ajustez la définition de la méthode FIND_CARGO_FLIGHT. Saisissez le paramètre de retour e_flight avec LCL_FLIGHT au lieu de LCL_CARGO_FLIGHT. Veillez à sauvegarder la modification.

    1. Adaptez le code comme suit :

      ABAP
      123456789
      METHODS find_cargo_flight IMPORTING i_airport_from_id TYPE /dmo/airport_from_id i_airport_to_id TYPE /dmo/airport_to_id i_from_date TYPE /dmo/flight_date i_cargo TYPE /lrn/plane_actual_load EXPORTING e_flight TYPE REF TO lcl_flight e_days_later TYPE i.
  2. Analysez l'utilisation du vol variable local dans l'implémentation de la méthode FIND_CARGO_FLIGHT.

    1. Pour afficher le type de la variable de référence Vol, placez le curseur sur le nom de la variable et appuyez sur F2.

  3. Analysez l'appel de la méthode FIND_CARGO_FLIGHT dans la méthode IF_OO_ADT_CLASSRUN~MAIN de votre classe globale.

    1. Pour afficher le type de la variable de référence cargo_flight, placez le curseur sur le nom de la variable et appuyez sur F2.

  4. Adaptez la définition de la méthode FIND_PASSENGER_FLIGHT de la même manière. Saisissez le paramètre de retour e_flight avec LCL_FLIGHT au lieu de LCL_PASSENGER_FLIGHT.

    1. Adaptez le code comme suit :

      ABAP
      123456789
      METHODS find_passenger_flight IMPORTING i_airport_from_id TYPE /dmo/airport_from_id i_airport_to_id TYPE /dmo/airport_to_id i_from_date TYPE /dmo/flight_date i_seats TYPE i EXPORTING e_flight TYPE REF TO lcl_flight e_days_later TYPE i.
  5. Activez le code pour vérifier que cette modification n'entraîne pas non plus d'erreurs de syntaxe.

    1. Appuyez sur Ctrl + F3 pour activer le code.

Tâche 3: Attribut générique

Dans la classe locale LCL_CARRIER, définissez un attribut d'instance privé vols dans lequel vous pouvez archiver des références aux instances de LCL_PASSENGER_FLIGHT et LCL_CARGO_FLIGHT en même temps. Renseignez l'attribut dans l'implémentation de la méthode CONSTRUCTOR avec le contenu combiné des attributs passager_flights et cargo_flights.

Étapes

  1. Dans la classe locale LCL_FLIGHT, utilisez un quickfix pour générer un type de table publique basé sur cette classe (nom suggéré pour le type de table : tab).

    1. Accédez à la définition de la classe locale LCL_FLIGHT.

    2. Dans l'instruction CLASS, placez le curseur sur lcl_flight et appuyez sur Ctrl + 1 pour appeler les solutions rapides disponibles.

    3. Dans la liste des solutions rapides disponibles, sélectionnez Générer type de table pour lcl_flight.

    4. La nouvelle instruction TYPES doit ressembler à ceci :

      ABAP
      1
      TYPES tab TYPE STANDARD TABLE OF REF TO lcl_flight WITH DEFAULT KEY.
  2. Dans la section privée de la classe locale LCL_CARRIER, définissez de nouveaux vols d'attributs d'instance. Saisissez-le avec le type de table que vous venez de créer dans la classe locale LCL_FLIGHT.

    1. Adaptez le code comme suit :

      ABAP
      1234
      DATA passenger_flights TYPE lcl_passenger_flight=>tt_flights. DATA cargo_flights TYPE lcl_cargo_flight=>tt_flights. DATA flights TYPE lcl_flight=>tab.
  3. Adaptez l'implémentation de la méthode CONSTRUCTOR de la classe locale LCL_CARRIER. Après avoir appelé la méthode GET_FLIGHTS_BY_CARRIER de LCL_PASSENGER_FLIGHT, ajoutez toutes les références d'objet stockées dans passager_flights aux vols.

    1. Passez à l'implémentation de la méthode CONSTRUCTOR dans la classe locale LCL_CARRIER.

    2. Avant l'instruction ENDMETHOD, ajoutez le code suivant :

      ABAP
      123
      LOOP AT passenger_flights INTO DATA(passflight). APPEND passflight TO flights. ENDLOOP.

      Si vous préférez une compréhension de table, ajoutez plutôt le code suivant :

      ABAP
      12345
      flights = VALUE #( BASE flights FOR pflight IN passenger_flights ( pflight ) ).
  4. De même, implémentez une boucle sur cargo_flights et ajoutez également ces références aux vols.

    1. Avant l'instruction ENDMETHOD, ajoutez le code suivant :

      ABAP
      123
      LOOP AT cargo_flights INTO DATA(cargoflight). APPEND cargoflight TO flights. ENDLOOP.

      Si vous préférez une compréhension de table, ajoutez plutôt le code suivant :

      ABAP
      12345
      flights = VALUE #( BASE flights FOR cflight IN cargo_flights ( cflight ) ).

Tâche 4: Accès générique et Down-Cast

Supprimez les attributs d'instance passager_flights et cargo_flights dans la classe locale LCL_CARRIER et remplacez-les par le nouvel attribut vols. Lorsque le code accède à des composants spécifiques des sous-classes, implémentez un transtypage descendant.

Étapes

  1. Dans la partie définition de la classe locale LCL_CARRIER, supprimez ou commentez la définition des attributs passager_flights et cargo_flights.

    1. Sélectionnez les deux instructions DATA et appuyez sur Ctrl + < pour ajouter un signe de commentaire devant chaque ligne de code :

      ABAP
      12
      * DATA passenger_flights TYPE lcl_passenger_flight=>tt_flights. * DATA cargo_flights TYPE lcl_cargo_flight=>tt_flights.
  2. Dans la méthode CONSTRUCTOR, utilisez des déclarations en ligne pour déclarer passager_flights et cargo_flights comme variables locales.

    1. Adaptez le code comme suit :

      ABAP
      1234567
      DATA(passenger_flights) = lcl_passenger_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ). DATA(cargo_flights) = lcl_cargo_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ).
  3. Après les appels de méthode avec les déclarations en ligne, renseignez un attribut d'instance privé avec le nombre de lignes dans la variable locale passager_flights (nom suggéré : pf_count).

    Astuce

    Implémentez d'abord la valorisation. Utilisez ensuite un correctif rapide pour créer la déclaration d'attribut.
    1. Adaptez le code comme suit :

      ABAP
      12345
      DATA(passenger_flights) = lcl_passenger_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ). pf_count = lines( passenger_flights ).
    2. Placez le curseur sur pf_count, appuyez sur Ctrl + 1 et sélectionnez Déclarer attribut pf_count.

  4. De même, renseignez un attribut d'instance privé avec le nombre de lignes dans la variable locale cargo_flights (nom suggéré : cf_count).

    1. Adaptez le code comme suit :

      ABAP
      12345
      DATA(cargo_flights) = lcl_cargo_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ). cf_count = lines( cargo_flights ).
    2. Placez le curseur sur cf_count, appuyez sur Ctrl + 1 et sélectionnez Déclarer attribut cf_count.

  5. Dans la méthode GET_OUTPUT, remplacez les fonctions LINES intégrées par les attributs que vous venez de définir.

    1. Adaptez le code comme suit :

      ABAP
      123456
      METHOD get_output. APPEND |{ 'Carrier Name:'(001) } { me->name }| TO r_result. APPEND |{ 'Passenger Flights:'(002) } { pf_count }| TO r_result. APPEND |{ 'Average free seats:'(003) } { get_average_free_seats( ) }| TO r_result. APPEND |{ 'Cargo Flights:'(004) } { cf_count }| TO r_result. ENDMETHOD.
  6. Dans la méthode FIND_CARGO_FLIGHT, remplacez l'attribut cargo_flights par l'attribut vols.

    1. Adaptez le code comme suit :

      ABAP
      12
      * LOOP AT me->cargo_flights INTO DATA(flight) LOOP AT me->flights INTO DATA(flight)
  7. Dans l'appel de la méthode GET_FREE_CAPACITY, ajoutez une expression CAST.

    1. Adaptez le code comme suit :

      ABAP
      1234
      IF connection_details-airport_from_id = i_airport_from_id AND connection_details-airport_to_id = i_airport_to_id * AND flight->get_free_capacity( ) >= i_cargo. AND CAST lcl_cargo_flight( flight )->get_free_capacity( ) >= i_cargo.
  8. Pour vous assurer de prendre uniquement en compte les instances de LCL_CARGO_FLIGHT, étendez la condition WHERE du LOOP.

    Astuce

    N'oubliez pas que dans les tables internes avec le type de ligne élémentaire, vous adressez le contenu avec le pseudo-composant table_line.
    1. Adaptez le code comme suit :

      ABAP
      123
      LOOP AT me->flights INTO DATA(flight) WHERE table_line->flight_date >= i_from_date AND table_line IS INSTANCE OF lcl_cargo_flight.
  9. Adaptez l'implémentation de la méthode FIND_PASSENGER_FLIGHT de la même manière.

    1. Adaptez le code comme suit :

      ABAP
      12345678910111213141516171819
      LOOP AT me->flights INTO DATA(flight) WHERE table_line->flight_date >= i_from_date AND table_line IS INSTANCE OF lcl_passenger_flight. DATA(connection_details) = flight->get_connection_details( ). IF connection_details-airport_from_id = i_airport_from_id AND connection_details-airport_to_id = i_airport_to_id AND CAST lcl_passenger_flight( flight )->get_free_seats( ) >= i_seats. DATA(days_later) = flight->flight_date - i_from_date. IF days_later < e_days_later. "earlier than previous one? e_flight = flight. e_days_later = days_later. ENDIF. ENDIF. ENDLOOP.
  10. Adaptez l'implémentation de la méthode GET_AVERAGE_FREE_SEATS. Utilisez les vols de la table interne dans la réduction de table et ajoutez une expression CAST et une condition WHERE à la réduction. Le nombre requis de vols passagers est stocké dans l'attribut pf_count créé ci-dessus.

    1. Adaptez le code comme suit :

      ABAP
      12345678910111213141516
      METHOD get_average_free_seats. * Table Reduction ******************************************************** * r_result = REDUCE #( * INIT i = 0 * FOR flight IN passenger_flights * NEXT i += flight->get_free_seats( ) * ) / lines( passenger_flights ). r_result = REDUCE #( INIT i = 0 FOR flight IN flights WHERE ( table_line IS INSTANCE OF lcl_passenger_flight ) NEXT i += CAST lcl_passenger_flight( flight )->get_free_seats( ) ) / pf_count. ENDMETHOD.
  11. Activez et testez votre classe globale comme application de console.

    1. Appuyez sur Ctrl + F3.

    2. Appuyez sur F9.