Translate

viernes, 22 de junio de 2012

Clases colaborativas en Delphi. Parte I


Es posible extender un módulo sin generar una ensalada de código, nombres de procedimientos y variables ?

Si es posible .. pero como ?

Supongamos que un modulo complejo requiere de constantes ampliaciones .. normalmente estas ampliaciones se agregan como metodos del modulo, generando un espacio propio para la descripcion y resolucion de cada nueva funcionalidad agregada.

Por ejemplo, si el modulo es un TDataModule, con varias fuentes de datos, tipicamente requerira para el procesamiento extendido de agregar nuevos metodos y variables de instancia. 

Si el modulo de datos implementa el modelo de una Orden Compra, esta podria ser cargada a partir de un Presupuesto. Por lo tanto habria que agregar cierta cantidad de funcionalidad relativa a la carga, como ser

        Cargar el  Presupuesto
        Cargar el Detalle de la Orden de Compra a partir del Presupuesto
        Actualizar Totales de la Orden de Compra
        Finalizar el proceso

Por supuesto es posible escribir un solo metodo que implemente toda esta funcionalidad. El precio ... una posible ensalada de código, dificil de extender (pero no necesariamente de leer)

Pero normalmente se escribe un metodo de cabecera y sub metodos que implementaran partes especificas de la funcionalidad requerida.

      TModuloDeDatos.CargarDePresupuesto(const nPresupuestoPar: Integer);
      begin
         try
              IniciaCargaDePresupuesto(nPresupuestoPar);
              CargaPresupuesto;
              CargaDetalleDePresupuesto;
              RecalculaTotales;
          finally
              FinalizaCargaDePresupuesto;
          end;
     end;

Este es el enfoque estructurado clasico, la resolucion de un problema se divide en partes y cada una de estas partes se ataca relativamente por separado.

Sin embargo al implementarse esta solucion dentro de una clase pre-existente, agranda esa clase introduciendo funcionalidad especifica, la que luego en la practica no es tan facil de modificar o extender sin impactar en la clase anfitriona.

A medida que el modelo de datos se extiende introduciendo mas y mas funcionalidad - por ejemplo cargar la Orden de Compra de una Solicitud en vez de un Presupuesto - el modelo como conjunto empieza a volverse cada vez mas confuso y dificil de mantener. 

En parte por la dificulta en ir inventando nombres nuevos, y en parte porque la reutilizacion de codigo comun a las distintas funcionalidades empieza a introducir la tendencia a duplicar codigo.

Existe una forma muy poco conocida de estructurar codigo sin introducir nuevos metodos .. o muy poco usada. Es la de las subrutinas. Esta es una funcionalidad basica de Pascal, verdaderamente notable.

Dada un metodo ..

    TClase.Metodo
    begin
    end;

Es posible escribir de manera inmediata lo siguiente:

TClase.Metodo;

   procedure Inicio;
   begin
   end;

   procedure Proceso;
   begin
   end;

   procedure Fin;
   begin
   end;

begin
   Inicio;
   try
      Proceso
   finally
      Fin;
   end;
end;

Mas aun, una subrutina puede anidar sub subrutinas y asi siguiendo ..

TClase.Metodo;

   procedure Proceso; 

     function Detecta: Boolean;
     begin
     end;

    procedure SeteaValores;
    begin
    end;

   begin
     with Tabla do
     begin
        while not Eof do
        begin
           if Detecta then
             SeteaValores;
          Next;
        end;
     end;   
   end;

begin
end;

Incluso mas !!! .. una subrutina puede ser sobrecargada ! .. O sea:

TClase.Metodo;

  procedure Proceso; overload;
  begin
  end;

  procedure Proceso(const nParametro: Integer); overload;
  begin
  end;

begin

end;

Las subrutinas llevan sus propias variables locales en el stack .. y no se confunden con las variables del mismo nombre de un nivel de anidamiento superior ..

TClase.Metodo(const nI: Integer);
var
  nJ: Integer;

   procedure Proceso(const nI,nJ: Integer);
   begin
     // El Parametro nI de Proceso .. no es el Parametro nI de TClase.Metodo
     // El Parametro nJ de Proceso ... no es la variable local nJ de TClase.Metodo
   end;

begin
end;

De esta forma es posible describir encapsulada y estructuradamente el espacio de un problema sin salir nunca de alcanze de un Metodo de Clase, agregando tantas variables locales y sub rutinas anidades como haga falta para describir adecuadamente el problema en cuestion. Mas aun .. este enfoque no introduce problemas semanticos, ya que los nombres de metodos y variables son estrictamente locales y no se confunden con nombres identicos de metodos y variables en la clase contenedora.

Sin embargo este enfoque muy rapido y practico tiene como inconveniente que si una subrutina de un metodo repite codigo de otra subrutina en otro metodo, estariamos introduciendo codigo repetido.

La solucion obvia, es ... subir el nivel de esa subrutina al de un metodo de la Clase contenedora.

O sea ..

TClase.MetodoUno;

   procedure Proceso;
   begin
      MetodoTres;
   end;

begin
end;

TClase.MetodoDos;

   procedure Proceso;
   begin
      MetodoTres;
   end;

begin
end;

TClase.MetodoTres;
begin
end;

El TClase.MetodoTres .. es entonces una suerte de recurso compartido hibrido del TClase.MetodoUno y TClase.MetodoDos;

Existe sin embargo un enfoque adicional que permite estructurar, encapsular y especializar el codigo de nueva funcionalidad agregada a una Clase, con un minimo de acople.

Sigue en la Parte II

No hay comentarios:

Publicar un comentario