Herencia en ABAP: INHERITING FROM, ABSTRACT y FINAL

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

La herencia es un concepto central de POO que permite la reutilizacion y extension de clases. En ABAP, la herencia se realiza con INHERITING FROM. ABSTRACT y FINAL controlan la extensibilidad.

Conceptos basicos

  • Herencia: Una clase (subclase) adopta propiedades de otra (clase base)
  • ABSTRACT: Clases/metodos que no pueden instanciarse/llamarse directamente
  • FINAL: Clases/metodos que no pueden heredarse/sobrescribirse mas
  • REDEFINITION: Sobrescribir metodos de la clase base en la subclase

Sintaxis

Herencia

CLASS <subclase> DEFINITION
INHERITING FROM <clase_base>.

Abstract

CLASS <nombre_clase> DEFINITION ABSTRACT.
METHODS: <nombre_metodo> ABSTRACT.
ENDCLASS.

Final

CLASS <nombre_clase> DEFINITION FINAL.
METHODS: <nombre_metodo> FINAL.
ENDCLASS.

Redefinition

CLASS <subclase> DEFINITION INHERITING FROM <clase_base>.
METHODS: <nombre_metodo> REDEFINITION.
ENDCLASS.

Ejemplos

1. Herencia simple

" Clase base
CLASS lcl_vehicle DEFINITION.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string,
get_brand RETURNING VALUE(rv_brand) TYPE string,
start.
PROTECTED SECTION.
DATA: mv_brand TYPE string.
ENDCLASS.
CLASS lcl_vehicle IMPLEMENTATION.
METHOD constructor.
mv_brand = iv_brand.
ENDMETHOD.
METHOD get_brand.
rv_brand = mv_brand.
ENDMETHOD.
METHOD start.
WRITE: / 'Vehiculo arrancando...'.
ENDMETHOD.
ENDCLASS.
" Subclase
CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string
iv_doors TYPE i,
get_doors RETURNING VALUE(rv_doors) TYPE i.
PRIVATE SECTION.
DATA: mv_doors TYPE i.
ENDCLASS.
CLASS lcl_car IMPLEMENTATION.
METHOD constructor.
" Llamar constructor de clase base
super->constructor( iv_brand ).
mv_doors = iv_doors.
ENDMETHOD.
METHOD get_doors.
rv_doors = mv_doors.
ENDMETHOD.
ENDCLASS.
" Uso
DATA: lo_car TYPE REF TO lcl_car.
lo_car = NEW #( iv_brand = 'BMW' iv_doors = 4 ).
WRITE: / lo_car->get_brand( ). " BMW (heredado)
WRITE: / lo_car->get_doors( ). " 4 (metodo propio)
lo_car->start( ). " Vehiculo arrancando... (heredado)

2. Sobrescribir metodos (REDEFINITION)

CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string
iv_doors TYPE i,
start REDEFINITION. " Sobrescribir metodo
PRIVATE SECTION.
DATA: mv_doors TYPE i.
ENDCLASS.
CLASS lcl_car IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_brand ).
mv_doors = iv_doors.
ENDMETHOD.
METHOD start.
" Implementacion propia
WRITE: / 'Auto arranca el motor...'.
" Opcional: Llamar metodo de clase base
" super->start( ).
ENDMETHOD.
ENDCLASS.
" Uso
DATA: lo_vehicle TYPE REF TO lcl_vehicle,
lo_car TYPE REF TO lcl_car.
lo_vehicle = NEW lcl_vehicle( 'Generic' ).
lo_vehicle->start( ). " Vehiculo arrancando...
lo_car = NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ).
lo_car->start( ). " Auto arranca el motor...

3. SUPER - Llamar clase base

CLASS lcl_electric_car DEFINITION INHERITING FROM lcl_car.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string
iv_doors TYPE i
iv_battery TYPE i,
start REDEFINITION.
PRIVATE SECTION.
DATA: mv_battery_capacity TYPE i.
ENDCLASS.
CLASS lcl_electric_car IMPLEMENTATION.
METHOD constructor.
" Llamar constructor de clase padre
super->constructor(
iv_brand = iv_brand
iv_doors = iv_doors
).
mv_battery_capacity = iv_battery.
ENDMETHOD.
METHOD start.
WRITE: / 'Verificacion de bateria...'.
WRITE: / |Capacidad: { mv_battery_capacity } kWh|.
" Llamar metodo de clase base
super->start( ).
ENDMETHOD.
ENDCLASS.
" Uso
DATA(lo_tesla) = NEW lcl_electric_car(
iv_brand = 'Tesla'
iv_doors = 4
iv_battery = 100
).
lo_tesla->start( ).
" Salida:
" Verificacion de bateria...
" Capacidad: 100 kWh
" Auto arranca el motor...

4. Clases abstractas

" Clase abstracta - no puede instanciarse
CLASS lcl_shape DEFINITION ABSTRACT.
PUBLIC SECTION.
" Metodo abstracto - DEBE implementarse en subclase
METHODS: calculate_area ABSTRACT
RETURNING VALUE(rv_area) TYPE f.
" Metodo concreto - puede heredarse
METHODS: get_name RETURNING VALUE(rv_name) TYPE string.
PROTECTED SECTION.
DATA: mv_name TYPE string.
ENDCLASS.
CLASS lcl_shape IMPLEMENTATION.
METHOD get_name.
rv_name = mv_name.
ENDMETHOD.
" calculate_area no tiene implementacion (abstracto)
ENDCLASS.
" Subclase concreta: Circulo
CLASS lcl_circle DEFINITION INHERITING FROM lcl_shape.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_radius TYPE f,
calculate_area REDEFINITION.
PRIVATE SECTION.
DATA: mv_radius TYPE f.
CONSTANTS: c_pi TYPE f VALUE '3.14159265'.
ENDCLASS.
CLASS lcl_circle IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_name = 'Circulo'.
mv_radius = iv_radius.
ENDMETHOD.
METHOD calculate_area.
rv_area = c_pi * mv_radius ** 2.
ENDMETHOD.
ENDCLASS.
" Subclase concreta: Rectangulo
CLASS lcl_rectangle DEFINITION INHERITING FROM lcl_shape.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_width TYPE f
iv_height TYPE f,
calculate_area REDEFINITION.
PRIVATE SECTION.
DATA: mv_width TYPE f,
mv_height TYPE f.
ENDCLASS.
CLASS lcl_rectangle IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_name = 'Rectangulo'.
mv_width = iv_width.
mv_height = iv_height.
ENDMETHOD.
METHOD calculate_area.
rv_area = mv_width * mv_height.
ENDMETHOD.
ENDCLASS.
" Uso
DATA: lt_shapes TYPE TABLE OF REF TO lcl_shape.
" lo_shape = NEW lcl_shape( ). " ERROR! Es abstracta!
APPEND NEW lcl_circle( 5 ) TO lt_shapes.
APPEND NEW lcl_rectangle( iv_width = 4 iv_height = 3 ) TO lt_shapes.
LOOP AT lt_shapes INTO DATA(lo_shape).
WRITE: / lo_shape->get_name( ), ': Area =', lo_shape->calculate_area( ).
ENDLOOP.
" Salida:
" Circulo: Area = 78.54
" Rectangulo: Area = 12.00

5. FINAL - Prevenir herencia

" Clase final - NO puede heredarse
CLASS lcl_singleton DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS: get_instance
RETURNING VALUE(ro_instance) TYPE REF TO lcl_singleton.
PRIVATE SECTION.
CLASS-DATA: go_instance TYPE REF TO lcl_singleton.
METHODS: constructor.
ENDCLASS.
CLASS lcl_singleton IMPLEMENTATION.
METHOD constructor.
" Constructor privado
ENDMETHOD.
METHOD get_instance.
IF go_instance IS NOT BOUND.
go_instance = NEW #( ).
ENDIF.
ro_instance = go_instance.
ENDMETHOD.
ENDCLASS.
" Esta linea causaria error de sintaxis:
" CLASS lcl_child DEFINITION INHERITING FROM lcl_singleton.
" ...
" ENDCLASS.

6. Metodos FINAL

CLASS lcl_base DEFINITION.
PUBLIC SECTION.
" Este metodo NO puede sobrescribirse
METHODS: critical_operation FINAL.
" Este metodo puede sobrescribirse
METHODS: normal_operation.
ENDCLASS.
CLASS lcl_base IMPLEMENTATION.
METHOD critical_operation.
WRITE: / 'Operacion critica - no modificable'.
ENDMETHOD.
METHOD normal_operation.
WRITE: / 'Operacion normal'.
ENDMETHOD.
ENDCLASS.
CLASS lcl_derived DEFINITION INHERITING FROM lcl_base.
PUBLIC SECTION.
" OK: normal_operation puede redefinirse
METHODS: normal_operation REDEFINITION.
" ERROR: critical_operation es FINAL
" METHODS: critical_operation REDEFINITION.
ENDCLASS.
CLASS lcl_derived IMPLEMENTATION.
METHOD normal_operation.
WRITE: / 'Operacion sobrescrita'.
ENDMETHOD.
ENDCLASS.

7. PROTECTED - Acceso para subclases

CLASS lcl_account DEFINITION.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_balance TYPE p,
get_balance RETURNING VALUE(rv_balance) TYPE p,
deposit IMPORTING iv_amount TYPE p.
PROTECTED SECTION.
" Solo visible para esta clase y subclases
DATA: mv_balance TYPE p DECIMALS 2.
METHODS: validate_amount IMPORTING iv_amount TYPE p
RETURNING VALUE(rv_valid) TYPE abap_bool.
PRIVATE SECTION.
" Solo visible para esta clase
DATA: mv_account_number TYPE string.
ENDCLASS.
CLASS lcl_account IMPLEMENTATION.
METHOD constructor.
mv_balance = iv_balance.
ENDMETHOD.
METHOD get_balance.
rv_balance = mv_balance.
ENDMETHOD.
METHOD deposit.
IF validate_amount( iv_amount ).
mv_balance = mv_balance + iv_amount.
ENDIF.
ENDMETHOD.
METHOD validate_amount.
rv_valid = xsdbool( iv_amount > 0 ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_savings_account DEFINITION INHERITING FROM lcl_account.
PUBLIC SECTION.
METHODS: add_interest IMPORTING iv_rate TYPE p.
ENDCLASS.
CLASS lcl_savings_account IMPLEMENTATION.
METHOD add_interest.
" Acceso a PROTECTED mv_balance posible
DATA(lv_interest) = mv_balance * iv_rate / 100.
" Acceso a PROTECTED validate_amount posible
IF validate_amount( lv_interest ).
mv_balance = mv_balance + lv_interest.
ENDIF.
" ERROR: mv_account_number es PRIVATE
" mv_account_number = '123'.
ENDMETHOD.
ENDCLASS.

8. Polimorfismo

" Diferentes tipos de vehiculos
CLASS lcl_motorcycle DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: start REDEFINITION.
ENDCLASS.
CLASS lcl_motorcycle IMPLEMENTATION.
METHOD start.
WRITE: / 'Moto arranca rugiendo...'.
ENDMETHOD.
ENDCLASS.
CLASS lcl_truck DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: start REDEFINITION.
ENDCLASS.
CLASS lcl_truck IMPLEMENTATION.
METHOD start.
WRITE: / 'Diesel de camion arranca...'.
ENDMETHOD.
ENDCLASS.
" Uso polimorfico
DATA: lt_fleet TYPE TABLE OF REF TO lcl_vehicle.
APPEND NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ) TO lt_fleet.
APPEND NEW lcl_motorcycle( 'Honda' ) TO lt_fleet.
APPEND NEW lcl_truck( 'MAN' ) TO lt_fleet.
" Todos los vehiculos arrancan - cada uno a su manera
LOOP AT lt_fleet INTO DATA(lo_vehicle).
lo_vehicle->start( ).
ENDLOOP.
" Salida:
" Auto arranca el motor...
" Moto arranca rugiendo...
" Diesel de camion arranca...

9. Verificacion de tipo con IS INSTANCE OF

DATA: lo_vehicle TYPE REF TO lcl_vehicle.
lo_vehicle = NEW lcl_car( iv_brand = 'Audi' iv_doors = 4 ).
" Verificacion de tipo
IF lo_vehicle IS INSTANCE OF lcl_car.
WRITE: / 'Es un auto'.
" Downcast para metodos especificos
DATA(lo_car) = CAST lcl_car( lo_vehicle ).
WRITE: / 'Puertas:', lo_car->get_doors( ).
ENDIF.
" Con CASE TYPE OF
CASE TYPE OF lo_vehicle.
WHEN TYPE lcl_car.
WRITE: / 'Auto reconocido'.
WHEN TYPE lcl_motorcycle.
WRITE: / 'Moto reconocida'.
WHEN TYPE lcl_truck.
WRITE: / 'Camion reconocido'.
WHEN OTHERS.
WRITE: / 'Tipo de vehiculo desconocido'.
ENDCASE.

10. Clase abstracta con patron Template Method

CLASS lcl_report DEFINITION ABSTRACT.
PUBLIC SECTION.
" Template Method - define el flujo
METHODS: execute FINAL.
PROTECTED SECTION.
" Hook Methods - a implementar por subclases
METHODS: fetch_data ABSTRACT,
process_data ABSTRACT,
display_output ABSTRACT.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD execute.
" Flujo fijo, no sobrescribible (FINAL)
WRITE: / 'Reporte inicia...'.
fetch_data( ).
process_data( ).
display_output( ).
WRITE: / 'Reporte finaliza.'.
ENDMETHOD.
ENDCLASS.
CLASS lcl_sales_report DEFINITION INHERITING FROM lcl_report.
PROTECTED SECTION.
METHODS: fetch_data REDEFINITION,
process_data REDEFINITION,
display_output REDEFINITION.
ENDCLASS.
CLASS lcl_sales_report IMPLEMENTATION.
METHOD fetch_data.
WRITE: / ' Cargando datos de ventas...'.
ENDMETHOD.
METHOD process_data.
WRITE: / ' Calculando ingresos...'.
ENDMETHOD.
METHOD display_output.
WRITE: / ' Mostrando reporte de ventas.'.
ENDMETHOD.
ENDCLASS.
" Uso
DATA(lo_report) = NEW lcl_sales_report( ).
lo_report->execute( ).
" Salida:
" Reporte inicia...
" Cargando datos de ventas...
" Calculando ingresos...
" Mostrando reporte de ventas.
" Reporte finaliza.

Jerarquia de herencia

lcl_vehicle (Clase base)
├── lcl_car
│ │
│ └── lcl_electric_car
├── lcl_motorcycle
└── lcl_truck

Resumen

ConceptoPalabra claveDescripcion
HerenciaINHERITING FROMClase extiende otra clase
SobrescribirREDEFINITIONReimplementar metodo de clase base
Llamar clase basesuper->Acceso a clase padre
AbstractoABSTRACTNo instanciable / debe implementarse
FinalFINALNo heredable / no sobrescribible
ProtegidoPROTECTEDVisible para clase y subclases

Notas importantes / Mejores practicas

  • Herencia para relaciones “es-un” (Auto es un Vehiculo).
  • Para relaciones “tiene-un” mejor usar composicion.
  • PROTECTED para atributos/metodos que subclases necesitan.
  • ABSTRACT para clases base que solo sirven como plantilla.
  • FINAL para clases/metodos que no deben modificarse.
  • Polimorfismo permite tratamiento uniforme de diferentes tipos.
  • super-> para llamar implementacion de clase base.
  • Verificar tipos con IS INSTANCE OF antes de CAST.
  • Preferir INTERFACE para acoplamiento debil.
  • Evitar jerarquias de herencia profundas (max. 2-3 niveles).