Factory Method en ABAP OO

Si llevas un tiempo trabajando con ABAP Objects, es muy probable que hayas oído hablar de los Factory o del Factory Method Pattern. Es un concepto muy utilizado en diseño de software, pero también uno de los peor entendidos al principio.

En este artículo vamos a ver

  • Qué es exactamente un Factory
  • Qué problema resuelve
  • Cómo se implementa en ABAP
  • Cuándo usarlo

¿Qué es un Factory?

Un Factory (fábrica) es un patrón de diseño creacional que encapsula la lógica de creación de objetos. Es decir, un factory es una forma controlada de crear objetos. La idea es que no creas objetos directamente con NEW o CREATE OBJECT, sino que le pides a una clase que los cree por ti.

En lugar de instanciar directamente con:

CREATE OBJECT lo_producto. o DATA(lo_producto) = NEW zcl_producto( iv_nombre = 'Laptop' ).

Se utiliza un método de clase:

DATA(lo_producto) = zcl_producto_factory=>create_product( iv_tipo = 'LAPTOP' ).

¿Qué problema resuelve el Factory?

Imagina que tienes una aplicación que gestiona diferentes tipos de documentos: facturas, pedidos, albaranes, etc.

Sin Factory

METHOD procesar_documento.
  CASE iv_tipo.
    WHEN 'FACTURA'.
      DATA(lo_doc) = NEW zcl_factura( ).
    WHEN 'PEDIDO'.
      lo_doc = NEW zcl_pedido( ).
    WHEN 'ALBARAN'.
      lo_doc = NEW zcl_albaran( ).
  ENDCASE.

  lo_doc->procesar( ).
ENDMETHOD.

Problemas:

  • La lógica de qué objeto crear está repartida por todo el código.
  • Si aparece un nuevo tipo de documento, tienes que modificar todos los lugares donde se crean documentos.
  • No hay control sobre cómo se crea el objeto

Con Factory

METHOD procesar_documento.
  DATA(lo_doc) = zcl_documento_factory=>crear( iv_tipo ).
  lo_doc->procesar( ).
ENDMETHOD.

Ventajas:

  • La creación está centralizada en un único lugar.
  • Añadir un nuevo tipo de documento solo requiere modificar la factory.
  • El código cliente es más simple, mantenible y no cambia.

Dos formas de implementar Factory en ABAP

Existen dos enfoques principales, y elegir uno u otro depende de la complejidad de tu escenario.

1. Método Factory dentro de la propia clase

Es la forma más común y sencilla. El método de creación está dentro de la misma clase que se va a instanciar.

CLASS zcl_producto DEFINITION.
  PUBLIC SECTION.
    DATA: nombre TYPE string.

    " Constructor público normal
    METHODS constructor IMPORTING iv_nombre TYPE string.

    " Método factory: crea instancias con lógica adicional
    CLASS-METHODS crear
      IMPORTING iv_tipo   TYPE string
      RETURNING VALUE(ro_producto) TYPE REF TO zcl_producto.
ENDCLASS.

CLASS zcl_producto IMPLEMENTATION.
  METHOD constructor.
    me->nombre = iv_nombre.
  ENDMETHOD.

  METHOD crear.
    " Aquí puedes aplicar lógica de creación
    CASE iv_tipo.
      WHEN 'BASICO'.
        ro_producto = NEW zcl_producto( 'Producto básico' ).
      WHEN 'PREMIUM'.
        ro_producto = NEW zcl_producto( 'Producto premium' ).
        " Lógica adicional para productos premium
      WHEN OTHERS.
        ro_producto = NEW zcl_producto( 'Producto genérico' ).
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

" Uso
DATA(lo_producto) = zcl_producto=>crear( 'PREMIUM' ).

Ventajas:

  • Simple y directa
  • No requiere clases adicionales
  • Ideal para el 80-90% de los casos

Cuándo usarla:

  • La lógica de creación es relativamente simple
  • Solo hay una clase principal con variantes
  • Quieres mantenerlo todo en un mismo lugar

2. Clase Factory separada

Cuando la lógica de creación se vuelve compleja o involucra múltiples clases relacionadas, conviene crear una clase factory independiente.

abap

" Interfaz común para todos los documentos
INTERFACE zif_documento.
  METHODS procesar.
  METHODS obtener_nombre RETURNING VALUE(rv_nombre) TYPE string.
ENDINTERFACE.

" Implementación concreta: Factura
CLASS zcl_factura DEFINITION.
  PUBLIC SECTION.
    INTERFACES zif_documento.
    METHODS constructor IMPORTING iv_numero TYPE string.
ENDCLASS.

" Implementación concreta: Pedido
CLASS zcl_pedido DEFINITION.
  PUBLIC SECTION.
    INTERFACES zif_documento.
    METHODS constructor IMPORTING iv_cliente TYPE string.
ENDCLASS.

" Clase Factory separada
CLASS zcl_documento_factory DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS crear
      IMPORTING iv_tipo          TYPE string
                iv_identificador TYPE string
      RETURNING VALUE(ro_documento) TYPE REF TO zif_documento.
ENDCLASS.

CLASS zcl_documento_factory IMPLEMENTATION.
  METHOD crear.
    CASE iv_tipo.
      WHEN 'FACTURA'.
        ro_documento = NEW zcl_factura( iv_identificador ).
      WHEN 'PEDIDO'.
        ro_documento = NEW zcl_pedido( iv_identificador ).
      WHEN OTHERS.
        RAISE EXCEPTION TYPE zcx_documento_invalido.
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

" Uso: el cliente trabaja con la interfaz, no con las clases concretas
DATA(lo_doc) = zcl_documento_factory=>crear( iv_tipo = 'FACTURA' iv_identificador = 'F-2024-001' ).
lo_doc->procesar( ).  " El cliente no sabe si es factura o pedido

Ventajas:

  • Máxima separación de responsabilidades
  • El cliente trabaja con interfaces, no con implementaciones concretas
  • Facilita pruebas unitarias (mockear la factory)

Cuándo usarla:

  • Necesitas reutilizar la misma lógica de creación en varios lugares
  • Hay múltiples clases que implementan una misma interfaz
  • La lógica de selección es compleja (ej. basada en configuración, BD, etc.)

Beneficios del patrón Factory

1. Creación controlada

La lógica de creación está en un único punto. Puedes añadir validaciones, registro de logs, o cualquier lógica adicional sin modificar el código cliente.

2. Abstracción

El cliente trabaja con interfaces, no con clases concretas. Esto reduce el acoplamiento y facilita cambios futuros.

3. Flexibilidad

Puedes cambiar la clase concreta que se devuelve sin afectar al cliente. Esto es clave para aplicar el principio de sustitución de Liskov.

4. Reutilización

Puedes implementar lógicas como:

  • Caché: Devolver instancias ya creadas
  • Singleton: Reutilizar la misma instancia siempre
  • Pool de objetos: Reutilizar instancias de un pool

El patrón Factory es una herramienta clave en diseño orientado a objetos en ABAP que te permite controlar cómo y cuándo se crean los objetos, reducir dependencias y hacer tu código más flexible y mantenible.