Análise e teste de código
Utilização correta de tipos de dados e conversões de tipo
Processamento de campos de caracteres
Uso de push-down de código no ABAP SQL
Melhorar o desempenho da tabela interna
Implementar verificações de autorização
Projetando código efetivo orientado a objetos
Definir e trabalhar com classes de exceção
Adicionar documentação à codificação ABAP

Utilizar herança

Objective

After completing this lesson, you will be able to utilizar a herança.

Up-cast e Down-Cast

Para instanciar uma subclasse, você declara uma variável de referência com o tipo da classe relevante. Em seguida, você usa o operador NEW para criar a instância. Você deve transferir valores para todos os parâmetros obrigatórios do construtor.

Utilizar referências de classe superior

Uma variável de referência com o tipo de uma classe superior (neste caso, lcl_plane) também pode conter referências a objetos com o tipo de uma subclasse. Isso faz sentido, uma vez que um avião de passageiros ou um avião de carga é um avião.

No entanto, uma variável de referência com o tipo de classe superior apenas conhece os componentes definidos na classe superior. Consequentemente, ao utilizar uma referência com este tipo para gerenciar uma instância de uma subclasse, só é possível acessar os componentes da classe superior, mesmo que o objeto real para o qual apontam os pontos de referência seja uma instância da subclasse.

Atribuir referências de objetos a uma variável de referência de um tipo diferente é chamado de casting. Atribuir uma referência que aponta para uma subclasse a uma variável de referência com o tipo de uma classe superior é chamado de up-cast, porque você está fazendo a atribuição para um tipo superior na hierarquia de herança.

Chamar métodos após um up-cast

Quando você usa uma variável de referência com o tipo ref to lcl_plane para gerenciar uma instância de avião de passageiros, você não pode usá-la para acessar componentes que pertencem à classe de avião de passageiros, pois a classe de avião não sabe que eles existem e a verificação de sintaxe não pode saber para que tipo de objeto a referência realmente apontará no momento da execução.

Utilizando a referência, você pode, contudo, acessar métodos cujas definições originais estejam contidas na classe. Este é o caso com o método get_attributes no exemplo da imagem. A questão aqui é qual implementação do método será chamada. Neste caso, o sistema consultará o tempo de execução para ver a que tipo de objeto a variável de referência está apontando, e chamará a implementação da classe correspondente. Em outras palavras, embora estejamos usando uma referência de avião para gerenciar uma instância da classe de avião de passageiros, ainda é a implementação de método da classe de avião de passageiros que é chamada.

Atribuir uma referência genérica a um tipo específico

Você sempre pode atribuir uma variável de referência com o tipo de uma subclasse para uma variável de referência com o tipo de uma classe superior. Por exemplo, você sempre pode atribuir uma referência de avião de passageiros a uma referência de avião. Como vimos, isso ocorre porque um avião de passageiros é um avião. Atribuir variáveis de referência na direção oposta, no entanto, é mais complicado. Isso ocorre porque uma referência de avião pode conter referências a outras classes além da classe de avião de passageiros. Em outras palavras, onde cada avião de passageiros é um avião, nem todo avião é um avião de passageiros.

Dadas referências de objeto plano com tipo ref to lcl_plane e passenger_plane com tipo ref to lcl_passenger_plane, o sistema sempre permitirá que você escreva plane = passenger_plane. No entanto, a atribuição passenger_plane = plane leva a um erro de sintaxe porque a verificação de sintaxe não pode ter certeza suficiente de que a referência de avião realmente terá uma referência a um objeto de avião de passageiros nesse momento.

O operador CAST

A verificação de sintaxe não permite que você atribua uma referência de classe superior diretamente a uma referência de subclasse. No entanto, você ainda pode fazê-lo usando o operador CAST. Casting significa olhar para um objeto como se ele tivesse um tipo diferente. No exemplo, dizemos ao sistema para tratar "avião" como se fosse uma instância da classe de avião de passageiros. Isso é sintaticamente correto, pois agora estamos fingindo que o "avião" no lado direito da expressão tem o mesmo tipo de "passageiro" no lado esquerdo. No entanto, ainda não existe nenhuma garantia de que o avião contém realmente uma referência de avião de passageiros e, se não contiver uma no momento da execução, a atribuição não pode ser efetuada. Se for deixada desprotegida, a atribuição poderá causar um erro de tempo de execução.

Proteger um down-cast

Se não estiver seguro de que o objeto para o qual uma variável de referência está apontando é realmente uma instância de subclasse correta, você pode verificar isso usando a expressão lógica IS INSTANCE OF. Neste exemplo, a atribuição à referência de avião de passageiros só é processada se o avião realmente apontar para uma instância da classe lcl_passenger_plane.

Experimente: Up-Cast e Down-Cast

  1. Crie uma nova classe que implemente a interface IF_OO_ADT_CLASSRUN.
  2. Mude para a guia Tipos locais e copie o seguinte snippet de código para o editor:
    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. Mude para a ficha Classe global e cole o seguinte snippet de código na implementação do método 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. Selecione CTRL + F3 para ativar a classe e F9 para executá-la como um aplicativo de console.
  5. Jogue com o código-fonte para se familiarizar com os conceitos.

Trabalhar com referências de classe superior

No exercício anterior, você criou uma classe superior comum LCL_FLIGHT para as classes LCL_PASSENGER_FLIGHT e LCL_CARGO_FLIGHT. Agora, você modificará a classe local LCL_CARRIER para utilizar a relação de herança. Você digitará variáveis de referência usadas como parâmetros para o tipo da classe superior e, em vez de dois atributos de instância separados passenger_flights e cargo_flights, definirá voos de atributo de instância única nos quais você pode armazenar referências a todas as instâncias de voo, independentemente de serem voos de passageiro ou de carga.

Modelo:

  • /LRN/CL_S4D401_OOS_INHERITANCE (classe global)

Solução:

  • /LRN/CL_S4D401_OOS_INH_USAGE (classe global)

Tarefa 1: Copiar modelo (opcional)

Copie a classe modelo /LRN/CL_S4D401_OOS_INHERITANCE. Se você tiver concluído o exercício anterior, pode ignorar esta tarefa e continuar processando sua classe ZCL_##_SOLUTION.

Etapas

  1. Copie a classe /LRN/CL_S4D401_OOS_INHERITANCE para uma classe em seu próprio pacote (nome sugerido: ZCL_##_SOLUTION, onde ## representa seu número de grupo).

    1. No Explorador de projetos, clique com o botão direito na classe /LRN/CL_S4D401_OOS_INHERITANCE para abrir o menu de contexto.

    2. No menu de contexto, selecione Duplicar....

    3. Insira o nome do seu pacote no campo Pacote. No campo Nome, insira ZCL_##_SOLUTION, em que ## representa seu número de grupo.

    4. Selecione Avançar.

    5. Confirme a ordem de transporte e selecione Concluir.

  2. Ative a cópia.

    1. Pressione Ctrl + F3 para ativar a classe.

Tarefa 2: Parâmetro genérico

Na classe local LCL_CARRIER, ajuste as definições dos métodos FIND_PASSENGER_FLIGHT e FIND_CARGO_FLIGHT. Nos dois métodos, digite o parâmetro de exportação e_flight como uma variável de referência com base na classe local LCL_FLIGHT.

Etapas

  1. Na classe local LCL_CARRIER, ajuste a definição do método FIND_CARGO_FLIGHT. Digite o parâmetro de retorno e_flight com LCL_FLIGHT em vez de LCL_CARGO_FLIGHT. Certifique-se de gravar a modificação.

    1. Ajuste o código da seguinte forma:

      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. Analise a utilização do voo de variável local na implementação do método FIND_CARGO_FLIGHT.

    1. Para exibir o tipo de voo da variável de referência, posicione o cursor no nome da variável e pressione F2.

  3. Analise a chamada do método FIND_CARGO_FLIGHT no método IF_OO_ADT_CLASSRUN~MAIN na sua classe global.

    1. Para exibir o tipo da variável de referência cargo_flight, posicione o cursor no nome da variável e pressione F2.

  4. Ajuste a definição do método FIND_PASSENGER_FLIGHT da mesma forma. Digite o parâmetro de retorno e_flight com LCL_FLIGHT em vez de LCL_PASSENGER_FLIGHT.

    1. Ajuste o código da seguinte forma:

      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. Ative o código para verificar se essa modificação também não causa erros de sintaxe.

    1. Pressione Ctrl + F3 para ativar o código.

Tarefa 3: Atributo genérico

Na classe local LCL_CARRIER, defina voos de atributo de instância privados nos quais você pode armazenar referências a instâncias de LCL_PASSENGER_FLIGHT e LCL_CARGO_FLIGHT ao mesmo tempo. Preencha o atributo na implementação do método CONSTRUCTOR com o conteúdo combinado dos atributos passenger_flights e cargo_flights.

Etapas

  1. Na classe local LCL_FLIGHT, utilize uma correção rápida para gerar um tipo de tabela pública baseado nessa classe (nome sugerido para o tipo de tabela: tab).

    1. Vá para a definição da classe local LCL_FLIGHT.

    2. Na instrução CLASS, posicione o cursor em lcl_flight e pressione Ctrl + 1 para chamar as correções rápidas disponíveis.

    3. Na lista de soluções rápidas disponíveis, selecione Gerar tipo de tabela para lcl_flight.

    4. A nova instrução TYPES deve ter o seguinte aspecto:

      ABAP
      1
      TYPES tab TYPE STANDARD TABLE OF REF TO lcl_flight WITH DEFAULT KEY.
  2. Na seção privada da classe local LCL_CARRIER, defina novos voos de atributo de instância. Digite o mesmo com o tipo de tabela que você acabou de criar na classe local LCL_FLIGHT.

    1. Ajuste o código da seguinte forma:

      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. Ajuste a implementação do método CONSTRUCTOR da classe local LCL_CARRIER. Depois de você ter chamado o método GET_FLIGHTS_BY_CARRIER de LCL_PASSENGER_FLIGHT, adicione todas as referências a objetos armazenadas em passenger_flights para voos.

    1. Vá para a implementação do método CONSTRUCTOR na classe local LCL_CARRIER.

    2. Antes da instrução ENDMETHOD, adicione o seguinte código:

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

      Se você preferir uma compreensão de tabela, adicione o seguinte código:

      ABAP
      12345
      flights = VALUE #( BASE flights FOR pflight IN passenger_flights ( pflight ) ).
  4. Da mesma forma, implemente um loop sobre cargo_flights e adicione essas referências a voos também.

    1. Antes da instrução ENDMETHOD, adicione o seguinte código:

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

      Se você preferir uma compreensão de tabela, adicione o seguinte código:

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

Tarefa 4: Acesso genérico e down-cast

Elimine os atributos de instância passenger_flights e cargo_flights na classe local LCL_CARRIER e substitua-os pelos novos voos de atributo. Quando o código acessa componentes específicos das subclasses, implemente um down-cast.

Etapas

  1. Na parte de definição da classe local LCL_CARRIER, remova ou comente a definição dos atributos passenger_flights e cargo_flights.

    1. Selecione as duas instruções DATA e pressione Ctrl + < para adicionar um sinal de comentário na frente de cada linha de código:

      ABAP
      12
      * DATA passenger_flights TYPE lcl_passenger_flight=>tt_flights. * DATA cargo_flights TYPE lcl_cargo_flight=>tt_flights.
  2. No método CONSTRUCTOR, utilize declarações inline para declarar passenger_flights e cargo_flights como variáveis locais.

    1. Ajuste o código da seguinte forma:

      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. Após a chamada do método com as declarações inline, preencha um atributo de instância privado com o número de linhas na variável local passenger_flights (nome sugerido: pf_count).

    Dica

    Implementar primeiro a avaliação. Em seguida, utilize uma correção rápida para criar a declaração de atributo.
    1. Ajuste o código da seguinte forma:

      ABAP
      12345
      DATA(passenger_flights) = lcl_passenger_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ). pf_count = lines( passenger_flights ).
    2. Posicione o cursor em pf_count, pressione Ctrl + 1 e selecione Declare attribute pf_count.

  4. Da mesma forma, preencha um atributo de instância privado com o número de linhas na variável local cargo_flights (nome sugerido: cf_count).

    1. Ajuste o código da seguinte forma:

      ABAP
      12345
      DATA(cargo_flights) = lcl_cargo_flight=>get_flights_by_carrier( i_carrier_id = i_carrier_id ). cf_count = lines( cargo_flights ).
    2. Posicione o cursor em cf_count, pressione Ctrl + 1 e selecione Declare attribute cf_count.

  5. No método GET_OUTPUT, substitua as funções LINES integradas pelos atributos que você acabou de definir.

    1. Ajuste o código da seguinte forma:

      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. No método FIND_CARGO_FLIGHT, substitua o atributo cargo_flights pelo atributo flights.

    1. Ajuste o código da seguinte forma:

      ABAP
      12
      * LOOP AT me->cargo_flights INTO DATA(flight) LOOP AT me->flights INTO DATA(flight)
  7. Na chamada do método GET_FREE_CAPACITY, adicione uma expressão CAST.

    1. Ajuste o código da seguinte forma:

      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. Para garantir que você só considera instâncias de LCL_CARGO_FLIGHT, amplie a condição WHERE de LOOP.

    Dica

    Lembre-se de que, em tabelas internas com o tipo de linha elementar, você aborda o conteúdo com pseudo-componente table_line.
    1. Ajuste o código da seguinte forma:

      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. Ajuste a implementação do método FIND_PASSENGER_FLIGHT da mesma forma.

    1. Ajuste o código da seguinte forma:

      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. Ajuste a implementação do método GET_AVERAGE_FREE_SEATS. Use os voos de tabela interna na redução de tabela e adicione uma expressão CAST e uma condição WHERE à redução. O número necessário de voos de passageiros é armazenado no atributo pf_count criado acima.

    1. Ajuste o código da seguinte forma:

      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. Ative e teste sua classe global como aplicativo de console.

    1. Pressione Ctrl + F3.

    2. Pressione F9.