mayo 13, 2006

CVS Snapshot de GLScene al 10 de mayo

Eric Grange ha puesto a la disposición pública un Snapshot del estado del desarrollo de GLScene al dia 10 de mayo del 2006.

Como puntos destacables están el que en este snapshot viene el paquete de instalación tanto para Delphi 2006 como para Lazarus, cabe mencionar que se han hecho grandes avances en el desarrollo sobre Lazarus. Creo que esto va a potenciar bastante el uso de Lazarus para aplicaciones gráficas.

En lo particular yo aún encuentro un poco "molesto" el trabajo con Lazarus, el ide aún me parece un tanto inestable y de comportamientos erráticos... me ha pasado por la cabeza iniciar una aplicación real de gestión sobre este IDE pero hasta ahora lo que he visto que se ha hecho me parece muy modesto en comparación con las posibilidades que ofrece Delphi.

En fin, larga vida a Lazarus!! pero que sea una vida de verdadero aprovechamiento, ojalá no se quede como el hermanito "wanabe" de Delphi.

mayo 04, 2006

Extendiendo la VCL sin usar herencia ni registrando componentes nuevos

Es común que de repente estemos tentados a instalar fulano componente porque hace X monería, el problema con eso es que si nos viciamos en esa practica, llevar los fuentes de nuestro proyecto a otra máquina tiene el inconveniente de que hay que instalar todos estos componentes antes de poder compilar la primera línea de código.

Existen algunas practicas que permiten variar el comportamiento de los componentes que trae Delphi por defecto sin hacer derivaciones de las clases registradas, lo cual permite que sin instalar nada en el IDE tengamos el resultado que deseamos. Una de esas técnicas la describo un poco en el artículo Alternativas a los Skins y es la técnica de las clases sobrepuestas, pero en Delphi 2006 esta no es la única opción... existe una técnica mas estilizada y que se apega más a las buenas costumbres orientadas a objetos.

En BDS 2006 y desde Delphi 8 existe una extensión del lenguaje de Object Pascal llamada Class Helpers esta nueva característica nos permite definir nuevos comportamientos para alguna clase previamente definida de manera natural y su práctica nos ofrece muchos beneficios. Vamos a un ejemplo:

Muchos de nosotros usamos la funcion Trim() de Delphi que elimina los espacios en blanco al principio y al final de una cadena, y solemos usarla en controles como el caso del TEdit con código como este:

Edit1.Text := Trim(Edit1.Text);

Pero, ¿no sería mas claro y elegante que el control Edit1 tuviera un método Ajustar() que hicera eso directamente sobre el control?, veamos como implementariamos eso, primero definimos la clase que servira como ayudante de la clase TEdit.

TMiEdit = class helper for TEdit
public
procedure Ajusta;
end;

He llamado a la clase auxiliar TMiEdit ya que en esta técnica la clase auxiliar no tiene que llevar el mismo nombre de la clase a la que estamos ayudando, y ahora definimos el método nuevo:

procedure TMiEdit.Ajusta;
begin
Text := Trim(Text);
end;

Observen que como esta clase es una ayudante de TEdit puedo acceder a las propiedades de la misma clase TEdit, en este caso la propiedad Text, ahora para hacer uso del nuevo método no tengo que crear nuevas instancias solo usar un objeto común de la clase TEdit de este modo:

procedure TForm2.Button1Click(Sender: TObject);
begin
Edit1.Ajusta;
end;

Pero tambien se pueden modificar métodos de la clase afectada, por ejemplo si quisieramos hacer que al llamar al método Clear del TEdit este en vez de poner una cadena nula pusiera por defecto un cero, hariamos algo como esto:

TMiEdit = class helper for TEdit
public
procedure Clear; overload;
end;

procedure TMiEdit.Clear;
begin
Text := '0';
end;


begin
Edit1.Clear;
end;

En este caso estamos sobrecargando el método Clear pero al ser este un método re-definido en la clase ayudante se toma este último por defecto.

Esto suele sernos útil en casos donde por ejemplo tenemos que ajustar controles como el TDBGrid para que soporte la ruedita del ratón, o para que cambie de casilla con la tecla Enter, o para Editar campos Memo en las celdas, etc... usando el control TDBGrid de toda la vida.

Las limitaciones que tiene esta técnica son que desde las clases ayudantes no se puede tener acceso a propiedades o métodos declarados como estrictamente privados o estrictamente protegidos, asi como tampoco permite la definición de datos adicionales, es decir que no se pueden declarar variables de campos en estas clases auxiliares; lo único que se permite es que se púeden definir variables de clase (una nueva característica tambien), es decir variables que serán comunes a todas las instancias de la clase ayudada, esto es útil para por ejemplo poder llevar la cuenta de cuantas instancias de X clase se han creado y de alguna manera implementar variantes del patrón de diseño singleton sobrecargando el constructor de la clase.