1
votes

Est-il possible de créer des types d'options dans ABAP?

Un inconvénient persistant dans ABAP est que l'on est incapable de distinguer une variable qui n'a jamais été définie de celle qui a été définie sur la valeur initiale de son type de données, et que les accès échoués aux tables internes via itab [.. .] les expressions lancent CX_SY_ITAB_LINE_NOT_FOUND ce qui est coûteux en termes d'exécution.

Pour les types énumérés, on peut définir la première variante comme invalide pour se prémunir contre la première confusion:

CLASS zcl_option DEFINITION
    FINAL.
PUBLIC SECTION.
  CLASS-METHODS some
    IMPORTING VALUE(val) TYPE any
    RETURNING VALUE(option) TYPE REF TO zcl_option.
  CLASS-METHODS none
    RETURNING VALUE(option) TYPE REF TO zcl_option.
  METHODS is_some
    RETURNING VALUE(is_some) TYPE abap_bool.
  METHODS get
    RETURNING VALUE(val) TYPE ???.
ENDCLASS.

(N'utilisez pas réellement ce type comme un remplacement ABAP_BOOL . La voie prédicative les appels de méthode se comportent, une méthode meth renvoyant un my_bool conduirait à obj-> meth () dans un IF obj-> meth (). n'étant véridique que si elle renvoie invalide , puisque l'appel de la méthode prédicative est équivalent à IF obj-> meth () IS NOT INITIAL. )

Pour les types non énumérés, nous n'avons pas de chance. Tentative de mise en œuvre de quelque chose comme un type d'option pour revenir à partir de méthodes qui ont essayé et échoué d'accéder à certaines données est bloqué par le faible typage des génériques:

TYPES:
  BEGIN OF ENUM my_bool,
    invalid,
    true,
    false,
  END OF ENUM my_bool.

où essayer d'utiliser un type générique dans la valeur de retour de get échoue avec "renvoyer les paramètres doit être entièrement tapé ".

Est-il possible de construire quelque chose comme le type d'option dans ABAP, ou même plus généralement types de somme ?


2 commentaires

Je pense qu'il n'y a pas de meilleure réponse que votre proposition, et vous pourriez obtenir uniquement des réponses avisées sur la façon de la coder de manière générique (par exemple en utilisant l'héritage et en définissant la valeur dans la sous-classe comme un attribut READ-ONLY, etc.) contexte dans lequel vous souhaitez l'utiliser, car il semble actuellement très théorique, afin d'obtenir des réponses plus précises.


@SandraRossi Bien que j'aie un contexte concret en tête, je trouverais un tel type généralement utile pour renvoyer à partir de méthodes qui interrogent des ressources qui peuvent ou non exister, c'est-à-dire où la non-existence n'est pas un état exceptionnel en tant que tel. (Exemple: récupérez l'entrée TADIR d'une classe, si elle existe. Par exemple, de nombreuses classes générées par des éléments comme le framework BSP n'en ont pas, mais ce n'est pas une erreur.)


3 Réponses :


1
votes

C'est ce que j'ai imaginé. J'étais sur le point de le jeter quand il a commencé à ne pas être 100 % inutile, alors voilà: D: D


PROGRAM ztest_options_tongue_in_cheek.

*&---------------------------------------------------------------------*
TYPES:
  BEGIN OF ENUM t_options,
    some,
    none,
  END OF ENUM t_options.

DEFINE macro_option_type.
  CLASS zcl_option_&1 DEFINITION FINAL.
    PUBLIC SECTION.
      METHODS:
        constructor
          IMPORTING VALUE(i_val)     TYPE &1
                    VALUE(i_option)  TYPE t_options,
        is_some
          RETURNING VALUE(e_is_some) TYPE abap_bool,
        get
          RETURNING VALUE(e_val)     TYPE &1.
    PRIVATE SECTION.
      DATA:
        lv_the_value TYPE &1,
        lv_is_none   TYPE abap_bool.
  ENDCLASS.

  CLASS zcl_option_&1 IMPLEMENTATION.
    METHOD constructor.
      lv_the_value = i_val.
      lv_is_none   = COND #( WHEN i_option = none THEN abap_true ELSE abap_false ).
    ENDMETHOD.

    METHOD is_some.
      e_is_some = COND #( WHEN lv_is_none = abap_false THEN abap_true ELSE abap_false ).
    ENDMETHOD.

    METHOD get.
      e_val = lv_the_value.
    ENDMETHOD.
  ENDCLASS.
END-OF-DEFINITION.
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* Static declaration of required option types
macro_option_type:
  i, string, char256, float, uzeit, datum.
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
DEFINE option.
* Rely on garbage collector for simplicity
  &2 = NEW zcl_option_&1( i_val = CONV &1( &3 ) i_option = &4 ).
END-OF-DEFINITION.
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
DEFINE declare_init_option.
  DATA &2 TYPE REF TO zcl_option_&1.
  option &1 &2 space none.
END-OF-DEFINITION.
*&---------------------------------------------------------------------*



*&---------------------------------------------------------------------*
START-OF-SELECTION.
*&---------------------------------------------------------------------*
  PERFORM test_options.


*&---------------------------------------------------------------------*
*& Form TEST_OPTIONS
*&---------------------------------------------------------------------*
FORM test_options .

  declare_init_option:
    i      lo_integer_option,
    string lo_string_option,
    float  lo_float_option,
    uzeit  lo_time_option.



  option i lo_integer_option 123 some.

  option string lo_string_option 'I am now a string' some.

  option string lo_string_option `` none. "back to none

* e.g.,
  IF lo_integer_option->is_some( ) = abap_true.
    WRITE: / |lo_integer_option is { lo_integer_option->get( ) }|.
  ELSE.
    WRITE: / 'lo_integer_option is nothing'.
  ENDIF.

ENDFORM.


0 commentaires

0
votes

Oui, c'est possible. Cependant, il n'est pas aussi sûr de type et donc pratique que dans les langages de programmation qui prennent en charge le typage générique.

METHODS do_something
  RETURNS
    VALUE(result) TYPE REF TO zcl_optional. " int4 or string?

utilisé comme

DATA(optional_integer) = NEW zcl_optional( 42 ).
cl_abap_unit_assert=>assert_true( optional_integer->is_available( ) ).

DATA integer_variable TYPE i.
optional_integer->put_into( REF #( integer_variable ) ).
cl_abap_unit_assert=>assert_equals( act = integer_variable exp = 42 ).

DATA(optional_string) = NEW zcl_optional( `abc` ).
cl_abap_unit_assert=>assert_true( optional_string->is_available( ) ).

DATA string_variable TYPE string.
optional_string->put_into( REF #( string_variable ) ).
cl_abap_unit_assert=>assert_equals( act = string_variable exp = `abc` ).

DATA(empty_optional) = NEW zcl_optional( ).
cl_abap_unit_assert=>assert_false( empty_optional->is_available( ) ).

DATA another_variable TYPE string.
empty_optional->put_into( REF #( another_variable ) ).
cl_abap_unit_assert=>assert_initial( another_variable ).

Le problème étant que les méthodes retournant un optionnel n'ont aucun moyen de décrire le type réel du contenu.

CLASS zcl_optional DEFINITION PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    METHODS constructor
      IMPORTING
        value TYPE simple OPTIONAL.
    METHODS is_available
      RETURNING
        VALUE(result) TYPE abap_bool.
    METHODS put_into
      IMPORTING
        reference TYPE REF TO data.

  PRIVATE SECTION.
    DATA value TYPE REF TO data.

ENDCLASS.

CLASS zcl_optional IMPLEMENTATION.

  METHOD constructor.
    IF value IS SUPPLIED.
      DATA(type_descriptor) =
        CAST cl_abap_datadescr( cl_abap_datadescr=>describe_by_data( value ) ).
      CREATE DATA me->value TYPE HANDLE type_descriptor.
      ASSIGN me->value->* TO FIELD-SYMBOL(<target>).
      <target> = value.
    ENDIF.
  ENDMETHOD.

  METHOD is_available.
    result = xsdbool( me->value IS BOUND ).
  ENDMETHOD.

  METHOD put_into.
    IF value IS BOUND.
      ASSIGN value->* TO FIELD-SYMBOL(<source>).
      ASSIGN reference->* TO FIELD-SYMBOL(<target>).
      <target> = <source>.
    ENDIF.
  ENDMETHOD.

ENDCLASS.


0 commentaires

0
votes

La valeur initiale de la table interne est tableau toujours vide sans lignes. Donc, toute votre idée n'a aucun sens.

La définition de la valeur initiale ne vous empêchera pas d'obtenir l'exception CX_SY_ITAB_LINE_NOT_FOUND .


0 commentaires