Estaba refrescando un poco mi memoria de programación orientada a objetos (OOP) en ABAP, y decidí hacer el siguiente ejemplo, usando los conceptos de Encapsulamiento, Herencia y Polimorfismo.
Haciendo un ejemplo de polimorfismo podemos de paso abarcar también Herencia y Encapsulamiento, por ejemplo, digamos que queremos tener una clase con los atributos de vehículos, por ejemplo autos y motos. Para hacer esto podríamos definir una clase “Vehículos” que abarque a varios tipos de valga la redundancia vehículos.
Primero definimos una clase abstracta llamada vehículo, una clase abstracta tiene la particularidad que solo necesitamos definir, la DEFINITION y no tiene IMPLEMENTATION. Esto lo usaremos de mano de la definición de métodos para lograr polimorfismo.
CLASS lcl_vehiculo DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS: obtener_tipo ABSTRACT,
obtener_marca ABSTRACT,
set_notas ABSTRACT
IMPORTING notas TYPE string,
ver_notas ABSTRACT
EXPORTING notas TYPE string.
PROTECTED SECTION.
DATA: lv_notas TYPE string.
ENDCLASS.
En el código de arriba podemos ver que definimos la clase junto con sus métodos (Públicos) y en la sección Protegida la variable notas (Aca tenemos un ejemplo de encapsulamiento, donde la variable al estar dentro de la sección PROTECTED solo puede ser accedida desde si misma y clases heredadas. Podríamos pensar de una clase abstracta como un template o una “receta” para luego definir otras clases.
El paso siguiente seria por ejemplo definir una clase para los objetos de tipo auto usando la que ya tenemos.
CLASS lcl_auto DEFINITION
INHERITING FROM lcl_vehiculo.
PUBLIC SECTION.
METHODS: obtener_tipo REDEFINITION,
obtener_marca REDEFINITION,
set_notas REDEFINITION,
ver_notas REDEFINITION,
cantidad_puertas.
ENDCLASS.
En el código de arriba definimos la clase lcl_auto y podemos ver la sentencia INHERITING FROM esto indica que estamos HEREDANDO la clase lcl_vehiculo, con todos sus métodos y variables.
Tambien podemos ver que esta clase (lcl_auto) tiene un método mas (cantidad_puertas), cada clase sea heredada o no puede tener sus propios métodos.
En los métodos podemos ver que tienen la palabra clave REDEFINITION, esto quiere decir que el método lo vamos a redefinir para que haga lo que nosotros deseemos. Ahora creamos la implementacion de la clase.
CLASS lcl_auto IMPLEMENTATION.
METHOD obtener_tipo.
WRITE 'AUTO'.
ENDMETHOD.
METHOD obtener_marca.
WRITE 'FORD'.
ENDMETHOD.
METHOD set_notas.
lv_notas = notas.
ENDMETHOD.
METHOD ver_notas.
WRITE lv_notas.
ENDMETHOD.
METHOD cantidad_puertas.
WRITE '4'.
ENDMETHOD.
ENDCLASS.
En la implementacion de cada método podemos poner lo que nosotros queremos que el mismo haga. Podemos ver particularmente en el método set_notas que estamos seteando el valor de la variable lv_notas, esto es posible porque la misma esta declarada como PROTECTED, con lo cual una clase heredada puede modificar su valor. Si en lugar de PROTECTED fuese PRIVATE esto no seria posible.
Ahora hacemos lo mismo que antes pero para un objeto moto.
Primero declaramos la definición y luego la implementacion.
CLASS lcl_moto DEFINITION
INHERITING FROM lcl_vehiculo.
PUBLIC SECTION.
METHODS: obtener_tipo REDEFINITION,
obtener_marca REDEFINITION,
set_notas REDEFINITION,
ver_notas REDEFINITION.
ENDCLASS.
CLASS lcl_moto IMPLEMENTATION.
METHOD obtener_tipo.
WRITE 'MOTO'.
ENDMETHOD.
METHOD obtener_marca.
WRITE 'HONDA'.
ENDMETHOD.
METHOD set_notas.
lv_notas = notas.
ENDMETHOD.
METHOD ver_notas.
WRITE lv_notas.
ENDMETHOD.
ENDCLASS.
Como podemos ver en el ejemplo de arriba esta clase no tiene el método cantidad_puertas.
Finalmente solo resta hacer la declaración de los objetos y su llamado.
Para declarar los objetos hacemos lo siguiente:
"Declaramos los objetos.
DATA: lo_autos TYPE REF TO lcl_auto,
lo_motos TYPE REF TO lcl_moto.
"Creamos los objetos.
CREATE OBJECT lo_autos.
CREATE OBJECT lo_motos.
Primero creamos dos variables que van a contener los objetos y después creamos los mismos.
El ultimo paso es la llamada a los métodos de los objetos
CALL METHOD lo_autos->set_notas( 'ALGO Autos' ).
CALL METHOD lo_motos->set_notas( 'ALGO 2 Motos').
WRITE 'TIPO:'.
CALL METHOD lo_autos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_autos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_autos->ver_notas( ).
NEW-LINE.
WRITE 'PUERTAS:'.
CALL METHOD lo_autos->cantidad_puertas( ).
NEW-LINE.
WRITE '**************'.
NEW-LINE.
WRITE 'TIPO:'.
CALL METHOD lo_motos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_motos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_motos->ver_notas( ).
Esto nos muestra como resultado:
El código completo:
*&---------------------------------------------------------------------*
*& Report ZPOLYTEST2
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT ZPOLYTEST2.
"Ejemplo de encapsulamiento, herencia y polimorfismo en ABAP.
"Definimos una clase abstracta, podriamos llamarla una plantilla
"para las clases heredadas. Cuando definimos una clase como abstracta
"no tiene implementacion.
CLASS lcl_vehiculo DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS: obtener_tipo ABSTRACT,
obtener_marca ABSTRACT,
set_notas ABSTRACT
IMPORTING notas TYPE string,
ver_notas ABSTRACT
EXPORTING notas TYPE string.
PROTECTED SECTION.
DATA: lv_notas TYPE string.
ENDCLASS.
***********************************************************************
"Definimos una clase lcl_auto que hereda lcl_vehiculo y redefine los
"metodos de la clase padre (lcl_vehiculo).
CLASS lcl_auto DEFINITION
INHERITING FROM lcl_vehiculo.
PUBLIC SECTION.
"Los metodos con REDEFINITION son los originales de lcl_vehiculo.
"Como podemos ver podemos agregar nuestros propios metodos
"como por ejemplo cantidad_puertas que pertenece solo a esta clase.
METHODS: obtener_tipo REDEFINITION,
obtener_marca REDEFINITION,
set_notas REDEFINITION,
ver_notas REDEFINITION,
cantidad_puertas.
ENDCLASS.
"Implementamos la clase lcl_auto con sus metodos.
CLASS lcl_auto IMPLEMENTATION.
METHOD obtener_tipo.
WRITE 'AUTO'.
ENDMETHOD.
METHOD obtener_marca.
WRITE 'FORD'.
ENDMETHOD.
METHOD set_notas.
lv_notas = notas.
ENDMETHOD.
METHOD ver_notas.
WRITE lv_notas.
ENDMETHOD.
METHOD cantidad_puertas.
WRITE '4'.
ENDMETHOD.
ENDCLASS.
***********************************************************************
"Definimos una clase lcl_moto que hereda lcl_vehiculo y redefine los
"metodos de la clase padre (lcl_vehiculo).
CLASS lcl_moto DEFINITION
INHERITING FROM lcl_vehiculo.
PUBLIC SECTION.
METHODS: obtener_tipo REDEFINITION,
obtener_marca REDEFINITION,
set_notas REDEFINITION,
ver_notas REDEFINITION.
ENDCLASS.
"Implementamos la clase lcl_moto con sus metodos.
CLASS lcl_moto IMPLEMENTATION.
METHOD obtener_tipo.
WRITE 'MOTO'.
ENDMETHOD.
METHOD obtener_marca.
WRITE 'HONDA'.
ENDMETHOD.
METHOD set_notas.
lv_notas = notas.
ENDMETHOD.
METHOD ver_notas.
WRITE lv_notas.
ENDMETHOD.
ENDCLASS.
***********************************************************************
START-OF-SELECTION.
"Declaramos los objetos.
DATA: lo_autos TYPE REF TO lcl_auto,
lo_motos TYPE REF TO lcl_moto.
"Creamos los objetos.
CREATE OBJECT lo_autos.
CREATE OBJECT lo_motos.
"Llamamos a los metodos que setean las notas.
CALL METHOD lo_autos->set_notas( 'ALGO Autos' ).
CALL METHOD lo_motos->set_notas( 'ALGO 2 Motos').
"Llamamos a los metodos que muestran la informacion.
WRITE 'TIPO:'.
CALL METHOD lo_autos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_autos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_autos->ver_notas( ).
NEW-LINE.
WRITE 'PUERTAS:'.
CALL METHOD lo_autos->cantidad_puertas( ).
NEW-LINE.
WRITE '**************'.
NEW-LINE.
WRITE 'TIPO:'.
CALL METHOD lo_motos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_motos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_motos->ver_notas( ).