Recordando la entrada anterior, en la que me cuestionaba la oportunidad de haber dotado una estructura clásica de nuestra programación, como puede ser la del registro (Record), de aspectos que podían acercarla notablemente a las clases, me llevó a profundizar un tanto sobre los motivos y ciertamente, tras la lectura de uno de los capítulos del libro “Delphi 2007 Handbook” de Marco Cantú, encontré que se aludían básicamente motivos de rapidez en la ejecución del código, por la forma en que se gestiona el acceso y la carga de la memoria en ambas estructuras. Los registros tienen menor coste en su gestión. Una variable local de registro, por poner un ejemplo, se gestiona en memoria desde la pila, motivo suficiente para que su coste sea menor que el soportado por una instancia de una clase que se ubica en una dirección de memoria y se gestiona desde el sistema de memoria (Memory Manager).

El ejemplo que pone M. Cantú para comentar como se sobrescriben los operadores creo que es claro y sencillo. Creo incluso que es el utilizado por todos los escritores conocidos para comentar estos aspectos. ya que la clase TPunto se presta como anillo al dedo para operaciones como cálculos de distancia asimilados en operadores básicos, como la suma (+), por poner el mas generalizado.

Ya sabéis que yo siempre me complico la existencia y cuando vi que ya me habían cogido el ejemplo del punto, ¡mierda! -exclame lamentándome- ¡ahora cual voy a coger yo…! Supuso que tuviera que activar las pocas neuronas libres y sanas que me quedan para inventarme algo que me sirviera para explicar cualquiera de los operadores redefinidos. Así que os pido perdón de forma anticipada si el ejemplo no es demasiado bueno ni claro. Se me ocurrió que una estructura de registro pudiera guardar tres precios distintos, que pudieran ser utilizados para calcular un total. Lo típico para no estrujarme demasiado la cabeza: Total es igual a cantidad por precio… jejeje 🙂

Y en este caso, hoy llevaba en mente comentar uno de los menos conocidos, que era el operador Explicito, que nos permite convertir un registro a un tipo definido previamente, mediante una conversión explicita. En el caso del ejemplo de Marco Cantú, convertía un registro TRecordPoint, en su formato de texto, mediante la conversión string(C) siendo C la estructura de registro mencionada. El resultado de la conversión era un mensaje en el que se escribían las coordenadas del punto.

En nuestro caso, la conversión nos permitirá acceder a los métodos de una clase… de forma que solo sea creada una instancia de la misma, inspirandome en el patrón singleton, salvando las diferencias de que esto es un ejemplo patatero y para ir por casa.

Veamos…

Podemos empezar definiendo los tres tipos de tarifa.

Tarifa = (fbasica, fDoble, fTriple);

y la estructura que va a utilizarla y que nos devolverá la instancia de la clase creada. Este array nos permitirá acceder a los objetos cuando deseemos liberar la memoria que han reservado.

 TArrayTarifa = Array[Tarifa] of TTarifa;

Ahora la estructura del registro…

  TTarifaRecord = record
  private
    ListaTarifas: TArrayTarifa;
  public
   class operator Explicit(var ATarifa: TTarifaRecord): TTarifaSimple;
   class operator Explicit(var ATarifa: TTarifaRecord): TTarifaDoble;
    class operator Explicit(var ATarifa: TTarifaRecord): TTarifaTriple;
    procedure Inicializar;
    procedure Eliminar;
  end;

A ver que se nos ocurre para el primer operador Explicit

class operator TTarifaRecord.Explicit(var ATarifa: TTarifaRecord): TTarifaSimple;
begin
    if ATarifa.ListaTarifas[fBasica] = Nil then begin
      Result:= TTarifaSimple.Create;
      Result.Precio:= PrecioBasico;
      Result.NombreTarifa:= 'Tarifa básica';
      ATarifa.ListaTarifas[fBasica]:= Result;
    end
    else
      Result:= TTarifaSimple(ATarifa.ListaTarifas[fBasica]);
end;

Fijaros que para que el código funcione correctamente, previamente debemos invocar el método inicializar que nos permite asignar a Nil la estructura interna del array, que debe albergar una referencia a la instancia creada. En este caso concreto y para que se vea que puede ser hecho de varias formas, he asignado manualmente cada uno de los elementos del array pero lo propio podría haber sido el uso de [for] para recorrer el array, mediante una variable local de tipo Tarifa que es una enumeración y por lo tanto puede ser definidos los valores minimo y máximo mediante funciones como High o Low. En el método Eliminar, por el contrario, utilizaremos una nueva funcionalidad propia de esta versión de Delphi, que es otra de las novedades, y que reside en el uso de [for __ in _____ do] para recorrer el array.

Veamos que hace Inicializar.

procedure TTarifaRecord.Inicializar;
begin
  ListaTarifas[fBasica]:= Nil;
  ListaTarifas[fDoble]:= Nil;
  ListaTarifas[fTriple]:= Nil;
end;

Y finalmente, podemos ver la novedad de su uso, en el método Eliminar.

procedure TTarifaRecord.Eliminar;
var
  Item: TTarifa;
begin
  for Item in ListaTarifas do begin
     if Assigned(Item) then begin
       Item.Free;
     end;
  end;
  Inicializar;
end;

Y ahora ya, solo nos queda inventarnos un ejemplo donde podamos combinar su uso y probar que funciona correctamente.

procedure TForm1.Button1Click(Sender: TObject);
var
  c: Integer;   //cantidad
  t1, t2, t3: Double;    //total
begin
  //inicializamos a nil el array
  tf.Inicializar;

   c:= 5;

   t1:= c * TTarifaSimple(tf).Precio;
   t2:= c * TTarifaDoble(tf).Precio;
   t3:= c * TTarifaTriple(tf).Precio;

   //visualizamos el primer momento
   Edit11.Text:= FormatFloat('0.00', t1);
   Edit21.Text:= FormatFloat('0.00', t2);
   Edit31.Text:= FormatFloat('0.00', t3);

   //vamos a por el segundo momento
   //asignamos los precios a cero en las tres tarifas
   TTarifaSimple(tf).Precio:= 0;
   TTarifaDoble(tf).Precio:= 0;
   TTarifaTriple(tf).Precio:= 0;

   t1:= c * TTarifaTriple(tf).Precio;
   t2:= c * TTarifaDoble(tf).Precio;
   t3:= c * TTarifaSimple(tf).Precio;

   //segundo momento deberia tomar valores 0 en las tres
   //dada la asignacion anterior
   Edit12.Text:= FormatFloat('0.00', t1);
   Edit22.Text:= FormatFloat('0.00', t2);
   Edit32.Text:= FormatFloat('0.00', t3);

   //y para finalizar comprobaremos el nombre de la tarifa...
   EditNombreTarifa1.Text:=  TTarifaSimple(tf).NombreTarifa;
   EditNombreTarifa2.Text:=  TTarifaDoble(tf).NombreTarifa;
   EditNombreTarifa3.Text:=  TTarifaTriple(tf).NombreTarifa;

   //aqui deberia eliminar los objetos creados
   tf.Eliminar;
end;

Respecto al uso de la construcción [for… in] podemos decir que se asimila al concepto de iterador que ya hemos usado anteriormente desde el código que hemos compartido (¿recordais el ejemplo del buscador de ficheros? Alli por ejemplo comentábamos su uso, que nos acercaba a uno de los patrones básicos de programación) . En este caso, C# introdujo la idea de esta construcción y Net Framework la ha promovido con la interfaz IEnumerator. Pero en nuestro caso, con Delphi 2007, la construcción se encuentra ya implementada en las estructuras básicas y puede ser usada en los caracteres de un string, en conjuntos, matrices y objetos que soporten GetEnumerator, que nos dara paso a que pueda ser usado en nuestra propias clases creadas.

En una próxima entrada hablaremos del operador Implicito y de algunos de los clásicos, esperando que no se me ocurra otra extravagancia como la aportada como ejemplo 😉 Lo importante al fin y al cabo es divertirse… 🙂

Descargar el ejemplo: Aquí

OF TOPICCCCC 🙂

¡Felicidades a la Selección Española de Futbol, la selección de mi país por el paso a las semifinales de la Eurocopa… ! ¡Increible! ¡Qué partidazo frente a Italia!

Plas!!! Plas!!! Plas!!!

Nos hemos quitado una espina que teníamos clavada desde hace un montonazo de años
Creo sinceramente que ha ganado el mejor.

Ya era hora que también nos sonriera la suerte!!!!!!!!!!!!!!
🙂

2 comentarios sobre “Algunas ventajas… (¡si eres capaz de recordarlas!) – Parte III

Los comentarios están cerrados.