Lección 14 · 14 de 20
Tipos procedurales y callbacks · Pascal
Pase funciones y métodos como parámetros en Object Pascal con tipos procedurales y of object, el mecanismo de los eventos de Lazarus.

Un tipo procedural describe la forma de una función: TPriceRule = function(price: Double): Double. Cualquier función con esa firma encaja, y Sale (un 20 % de descuento) y Clearance (un 50 %) la cumplen. En modo objfpc, `@Sale` obtiene la referencia a la función para pasarla como un valor más.
Checkout recibe la regla de precio y la llama como si fuera una función normal: rule(price). Así la misma rutina de cobro sirve para cualquier descuento presente o futuro sin tocar su código; es lo que se llama un callback.
`of object` cambia el juego: TSaleEvent no apunta a una función suelta, sino a un método de un objeto concreto. Por dentro guarda dos cosas, el código y el objeto (Self). Por eso @till.ShowSale lleva consigo a till y el método puede escribir su Name, Till 1.
Antes de llamar a un evento conviene comprobar `Assigned(onSale)`: si nadie se ha suscrito vale nil y llamarlo provocaría un fallo. Así funcionan los eventos de Lazarus: OnClick es un procedure(Sender: TObject) of object, y en las aplicaciones de escritorio de DMarlon Studio cada botón engancha un método del formulario exactamente igual.
Código · main.pas
program main;type TPriceRule = function(price: Double): Double; // any function of this shape TSaleEvent = procedure(price, total: Double) of object; // a method + its object TTill = class Name: String; procedure ShowSale(price, total: Double); end;procedure TTill.ShowSale(price, total: Double);begin WriteLn('[', Name, '] ', price:0:2, ' -> ', total:0:2);end;function Sale(price: Double): Double;begin Result := price * 0.80;end;function Clearance(price: Double): Double;begin Result := price * 0.50;end;procedure Checkout(price: Double; rule: TPriceRule; onSale: TSaleEvent);begin if Assigned(onSale) then onSale(price, rule(price)); // only if someone listensend;var till: TTill;begin till := TTill.Create; till.Name := 'Till 1'; Checkout(59.90, @Sale, @till.ShowSale); Checkout(59.90, @Clearance, @till.ShowSale); till.Free;end.Salida · fpc main.pas && ./main
[Till 1] 59.90 -> 47.92 [Till 1] 59.90 -> 29.95
Para recordar
- Un tipo procedural es la firma de una función.
@Funcionobtiene la referencia en modoobjfpc.of objectguarda un método y el objeto al que pertenece.- Compruebe
Assignedantes de llamar a un evento. - Los eventos de Lazarus (
OnClick…) son métodosof object.
Ejercicio
Escriba una regla Member que reste 5 EUR a precios de más de 30, cree un segundo TTill llamado 'Till 2' y cobre con él 59.90 usando @Member.