DMarlon Studio

Lección 6 · 6 de 20

Interfaces · Pascal

Defina interfaces en Object Pascal, impleméntelas en clases sin parentesco y deje que el recuento de referencias libere los objetos solo.

Pascal · Interfaces en DMarlon Studio: el código del ejemplo y su salida en la terminal

Una interfaz es un contrato: una lista de métodos sin datos ni código. ITax solo dice que hay una función Apply(net). Por convención, su nombre empieza por I.

Las clases TVat (IVA peninsular, 21 %) y TIgic (IGIC canario, 7 %) implementan la interfaz: la nombran tras la clase madre, class(TInterfacedObject, ITax), y escriben su propio Apply. No están emparentadas entre sí; lo único que comparten es el contrato. Una clase hereda de una sola madre, pero puede implementar varias interfaces.

Quote recibe un parámetro de tipo ITax, así que acepta cualquier objeto que cumpla el contrato, venga de la clase que venga. Es la manera de escribir código que no depende de clases concretas.

`TInterfacedObject` aporta el recuento de referencias: cada variable o parámetro de tipo interfaz que apunta al objeto suma uno, y cuando el contador vuelve a cero el objeto se libera solo. Por eso Quote('Madrid', TVat.Create, 250) no necesita Free: el objeto desaparece al terminar la llamada. La variable tax mantiene vivo el suyo hasta tax := nil.

La regla de oro: un objeto que se maneja por interfaz no se libera con Free ni se guarda a la vez en una variable de clase. Si necesita Supports o el operador as con interfaces, añádales un GUID entre corchetes.

Código · main.pas

program main;type  ITax = interface    function Apply(net: Double): Double;  end;  TVat = class(TInterfacedObject, ITax)    function Apply(net: Double): Double;  end;  TIgic = class(TInterfacedObject, ITax)    function Apply(net: Double): Double;  end;function TVat.Apply(net: Double): Double;begin  Result := net * 1.21;  // mainland Spainend;function TIgic.Apply(net: Double): Double;begin  Result := net * 1.07;  // Canary Islandsend;procedure Quote(const region: String; tax: ITax; net: Double);begin  WriteLn(region:9, ': ', net:7:2, ' net -> ', tax.Apply(net):7:2, ' EUR');end;var tax: ITax;begin  Quote('Madrid', TVat.Create, 250);     // the object is freed after the call  Quote('Tenerife', TIgic.Create, 250);  tax := TIgic.Create;                   // kept alive while tax refers to it  Quote('La Palma', tax, 19.99);  tax := nil;                            // last reference gone: freed hereend.

Salida · fpc main.pas && ./main

   Madrid:  250.00 net ->  302.50 EUR
 Tenerife:  250.00 net ->  267.50 EUR
 La Palma:   19.99 net ->   21.39 EUR

Para recordar

  • Una interfaz es una lista de métodos que una clase promete cumplir.
  • Una clase puede implementar varias interfaces.
  • Un parámetro de interfaz acepta cualquier clase que la implemente.
  • Con TInterfacedObject, el objeto se libera solo al quedarse sin referencias.
  • No mezcle Free con objetos manejados por interfaz.

Ejercicio

Cree TExempt = class(TInterfacedObject, ITax) para exportaciones, cuyo Apply devuelva el neto sin impuesto, y añada una línea Quote('Export', TExempt.Create, 250).