Boletín Kylix #3
INDICE
1. UNAS PALABRAS DEL EDITOR
2. INTRODUCCION AL OBJECT PASCAL DE BORLAND (II)
3. CUESTIONES DE PORTABILIDAD: SISTEMA DE ARCHIVOS
4. ¿QUE SIGUE?
________________________________________________________________________
1. UNAS PALABRAS DEL EDITOR
Debido a que no ha habido oposición, esta será la última edición del
Boletín Kylix. Todos los suscriptores de este boletín y del Boletín
Delphi quedarán automáticamente subscriptos al nuevo Boletín Pascal que
reemplazará ambas publicaciones.
Sus contribuciones, comentarios, críticas y preguntas son bienvenidas.
¡Manténganse en contacto!
Atentamente,
Ernesto De Spirito
eds2004 @ latiumsoftware.com
________________________________________________________________________
JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más
de 40 componentes integrados y personalizables. Múltiples problemas de
programación resueltos. Administración centralizada de recursos. Para
Delphi 3-6 y C++ Builder 3-5. http://www.jfactivesoft.com/spindex.htm
________________________________________________________________________
2. INTRODUCCION AL OBJECT PASCAL DE BORLAND (II)
Este artículo es el segundo de una introducción a la POO en Delphi. No le
enseñará POO --asumimos que ya se encuentra familiarizado con ella--,
pero le mostrará lo básico de trabajar con clases en Borland Object
Pascal.
Las clases se declaran en las declaraciones de tipos (type) de un
programa o en la sección de interfaz (interface) de una unidad. Una clase
de compone de miembros que pueden ser campos de datos, métodos y
propiedades.
Veámoslo con un ejemplo de una clase que llamaremos TRectangulo y que
representa un rectángulo:
type
TRectangulo = class
X1, Y1, X2, Y2: single;
function getAncho: single;
function getAltura: single;
procedure setAncho(Ancho: single);
procedure setAltura(Altura: single);
end;
Esta clase tiene cuatro campos (X1, Y1, X2 and Y2) que representan las
coordenadas de las esquinas del rectángulo, y cuatro métodos para
obtener/establecer las dimensiones del rectángulo. En la sección de
implementación (implementation) estos métodos se podrían definir como
sigue:
function TRectangulo.getAltura: single;
begin Result := Y2 - Y1; end;
function TRectangulo.getAncho: single;
begin Result := X2 - X1; end;
procedure TRectangulo.setAltura(Altura: single);
begin Y2 := Y1 + Altura; end;
procedure TRectangulo.setAncho(Ancho: single);
begin X2 := X1 + Ancho; end;
Declarar objetos de una clase es igual a declarar variables de cualquier
tipo. Por ejemplo:
var
r1, r2: TRectangulo;
Aquí hemos declarado dos objetos de la clase TRectangulo. En C++ uno está
limitado a declarar objetos dentro de funciones. La memoria se asigna
para esos objetos en la pila y sus constructores predeterminados son
llamados al entrar a la función. Asimismo, los destructores son llamados
automáticamente al volver de la función. Ese no es el caso en Object
Pascal. Las variables objeto son referencias (como punteros, pero no se
necesita el operador de derreferencia ^) así que usan espacio mínimo en
el segmento de datos o en la pila, y uno debe explícitamente crear y
liberar los objetos. Por ejemplo:
r1 := TRectangulo.Create; // Crea el objeto
r1.X1 := 10;
r1.Y1 := 35;
r1.X2 := 50;
r1.Y2 := 60;
ShowMessage('Ancho = ' + FloatToStr(r1.getAncho) + #13
+ 'Altura = ' + FloatToStr(r1.getAltura));
r1.Free; // Libera el objeto
Cuando crea un objeto, su memoria es asignada en el montículo y obtiene
de vuelta una referencia que debería asignar a una variable objeto. Luego
debería liberar la memoria llamando al método Free. Ahora bien, no
declaramos Create ni Free, así que ¿de donde salieron? Son métodos de
TObject y toda clase es descendiente de TObject, así que toda clase tiene
disponibles esos métodos. Entonces,
TRectangulo = class
y
TRectangulo = class(TObject)
son la misma cosa: TRectangulo es un descendiente directo de TObject. Use
esta sintaxis si desea que una clase herede de otra:
type
clase2 = class(clase1)
<declaraciones de miembros>
end;
Aquí clase2 hereda de clase1, o puede decirse que es una clase derivada
de clase1, o que clase1 es la clase base de clase2, etc.
Ahora, dijimos que además de campos y métodos, una clase podía tener
propiedades. ¿Qué es una propiedad? Es una construcción conveniente para
permitir un mayor nivel de ocultamiento. Para el programador que usa la
clase, las propiedades se ven como si fueran campos, pero en realidad
pueden ser campos o llamadas a métodos. Por ejemplo, agreguemos dos
propiedades a nuestra clase TRectangulo:
type
TRectangulo = class
...
property Ancho: single read getAncho write setAncho;
property Altura: single read getAltura write setAltura;
end;
De esta manera, un código como el siguiente:
r1.Ancho := 20;
r1.Altura := 10;
ShowMessage('Ancho = ' + FloatToStr(r1.Ancho) + #13
+ 'Altura = ' + FloatToStr(r1.Altura));
sería automáticamente traducido por el compilador a:
r1.setAncho(20);
r1.setAltura(10);
ShowMessage('Ancho = ' + FloatToStr(r1.getAncho) + #13
+ 'Altura = ' + FloatToStr(r1.getAltura));
Los miembros de una clase pueden ser privados, protegidos o públicos. Los
miembros privados son sólo accesibles desde dentro de la unidad en la que
se declara la clase, de modo que todas las clases dentro de una unidad
son "amigas" en la jerga del C++. Un miembro protegido es como un miembro
privado, excepto que puede también accederse desde descendientes de la
clase aunque estén en unidades distintas. Los miembros públicos son
siempre visibles. También puede haber miembros publicados que son
miembros públicos para los que se genera información RTTI (RunTime Type
Introspection: introspección de tipos en tiempo de ejecución) permitiendo
a una aplicación consultar los campos y propiedades de un objeto para
localizar sus métodos en tiempo de ejecución. Delphi usa RTTI para
acceder a los valores de propiedades cuando guarda o carga archivos de
formulario (.DFM), para mostrar propiedades en el Inspector de Objetos y
para asociar métodos específicos (controladores o manejadores de eventos)
con propiedades específicas (eventos) así que los miembros publicados son
muy comunes al escribir componentes visuales
Aquí va un ejemplo de una clase que usa todos estos tipos de miembros:
{$TYPEINFO ON}
TRectangulo = class
private
FX1, FY1, FX2, FY2: single;
protected
function getAncho: single;
function getAltura: single;
procedure setAncho(Ancho: single);
procedure setAltura(Altura: single);
public
function getSuperficie: single;
published
property X1: single read FX1 write FX1;
property Y1: single read FY1 write FY2;
property X2: single read FX2 write FX2;
property Y2: single read FY2 write FY2;
property Ancho: single read getAncho write setAncho;
property Altura: single read getAltura write setAltura;
property Superficie: read getSuperficie;
end;
{$TYPEINFO OFF}
Usamos una directiva del compilador para activar y desactivar la RTTI.
Como puede ver, la propiedad Superficie es de sólo lectura.
Una clase puede tener un constructor y un destructor:
type
TFile = class
private
FFile: File;
public
constructor Create(NomFich: string);
destructor Destroy(); override;
end;
implementation
constructor TFile.Create(NomFich: string);
begin
Assign(FFile, NomFich);
Reset(FFile, 1);
end;
destructor TFile.Destroy;
begin
Close(FFile);
end;
En este ejemplo, el constructor Create redefine al constructor Create
predeterminado de TObject y toma como parámetro el nombre del fichero que
será abierto. El destructor Destroy cierra el fichero. Debemos usar un
destructor para liberar todos los recursos asignados por un objeto.
Una clase puede tener muchos constructores, por ejemplo sobrecargando el
método Create o declarando nuevos constructores:
type
TFile = class
private
FFile: File;
public
constructor Create(NomFich: string); overload;
constructor Create(NomFich: string;
Modo: integer); overload;
constructor Crear(NomFich: string;
Tamanio: integer); overload;
destructor Destroy(); override;
end;
Para permitir el polimorfismo, un método puede ser declarado
virtual o dinámico en una clase base y luego sobredeclarado si es
necesario en las clases derivadas. Por ejemplo:
interface
type
TFigura = class(TObject)
procedure Mostrar; virtual;
end;
TCirculo = class(TFigura)
procedure Mostrar; override;
end;
TCuadrado = class(TFigura)
procedure Mostrar; override;
end;
TOvalo = class(TCirculo)
procedure Mostrar; override;
end;
implementation
procedure TFigura.Mostrar; begin ShowMessage('Figura'); end;
procedure TCirculo.Mostrar; begin ShowMessage('Circulo'); end;
procedure TCuadrado.Mostrar;begin ShowMessage('Cuadrado'); end;
procedure TOvalo.Mostrar; begin ShowMessage('Ovalo'); end;
TFigura es la clase base de TCirculo y TCuadrado, y declara un método
virtual (Mostrar) que es sobredeclarado (con override) en sus descen-
dientes. El verdadero método Mostrar que será llamado dependerá del
verdadero objeto al que una variable TFigura esté referenciando al
momento de la llamada, así que se determina en tiempo de ejecución:
var
Figura: TFigura;
Circulo: TCirculo;
Cuadrado: TCuadrado;
Ovalo: TOvalo;
begin
// Primero creamos los objetos
Circulo := TCirculo.Create;
Cuadrado := TCuadrado.Create;
Oval := TOval.Create;
Figura := Circulo; // Figura ahora referencia un objeto TCirculo
Figura.Mostrar; // Llama a TCirculo.Mostrar
Figura := Cuadrado; // Figura ahora referencia un objeto TCuadrado
Figura.Mostrar; // Llama a TCuadrado.Mostrar
Figura := Ovalo; // Figura ahora referencia un objeto TOvalo
Figura.Mostrar; // Llama a TOvalo.Mostrar
// Libera los objetos
Circulo.Free; Cuadrado.Free; Ovalo.Free;
end;
Si Mostrar no se hubiera declarado como virtual, hubiese sido un método
estático y por consiguiente en el ejemplo anterior TFigura.Mostrar
hubiera sido llamado tres veces.
En vez de "virtual" se puede usar la palabra reservada "dynamic". La
única diferencia es que virtual está optimizada para velocidad de
ejecución mientras que dynamic está optimizada para reducir el tamaño del
código, así que --por razones de rendimiento-- virtual es preferido para
implementar polimorfismo mientras que dynamic se usa sólo en los raros
casos en que una clase base tiene métodos virtuales que son ocasional-
mente sobredefinidos por sus clases derivadas.
Continuaremos con POO en Object Pascal en la próxima edición (en la
primera edición del Pascal Newsletter en realidad).
________________________________________________________________________
3. CUESTIONES DE PORTABILIDAD: SISTEMA DE ARCHIVOS
Este no será un gran dolor de cabeza, pero tenga aspirinas a mano por las
dudas. :-) Los sistemas de archivo de Linux y Windows son diferentes y
debe tener este hecho en cuenta si quiere hacer que sus aplicaciones sean
portables.
Sensibilidad a mayúsculas/minúsculas en nombres de archivos
===========================================================
Los nombres de archivos de Linux son sensibles a mayúsculas/minúsculas,
así que LEAME.TXT, LEAME.txt, Leame.txt y leame.txt son nombres
diferentes y por lo tanto pueden coexistir en el mismo directorio,
mientras que en Windows no sería posible dado que trata todos esos
nombres como uno solo.
En Windows puede abrir un fichero llamado Leame.txt usando LEAME.TXT en
su lugar, pero esto no sucederá en Linux. Debe usar el nombre exacto
(Leame.txt) si quiere abrir ese fichero. Para ser portable, una
aplicación debe preservar las mayúsculas y minúsculas en los nombres de
ficheros.
Otra solución podría ser siempre convertir los nombres de ficheros a
minúsculas. El único problema con esta técnica es que si a un usuario
avanzado de Linux le solicita un nombre de fichero pensará que HOLA, Hola
y hola son nombres de ficheros distintos mientras que su aplicación los
convertirá siempre a hola y eso puede significar una molestia... aunque
también podría ser una bendición para los novatos de Linux acostumbrados
a Windows, así que debe considerar cuidadosamente la audiencia de su
aplicación y si las distinciones mayúsculas y minúsculas son importantes
o si es convientes permitir mezclarlas o no.
Separador de caminos
====================
Esta es otra diferencia a tener en cuenta. En Windows, el separador es la
barra invertida ("\") mientras que en Linux es la barra normal ("/").
Delphi para Linux y Delphi 6 para Windows tendrán una constante prede-
finida llamada PathSeparator que contendrá el caracter de separación de
caminos apropiado, así que debería traducir código como este:
AssignFile(f, 'datos\arch001.dat');
en código como este:
AssignFile(f, 'datos' + PathSeparator + 'arch001.dat');
para hacer su código portable.
Si usa una versión anterior de Delphi, entonces debería definir
PathSeparator usted mismo. Una forma de hacerlo es definiendo
una simple unidad:
unit LinuxCompatibility;
interface
const PathSeparator: char = '\';
implementation
end.
Entonces, en cada unidad debería agregar LinuxCompatibility en la
cláusula uses para la compilación Win32:
{$IFDEF Win32}
uses
Windows, ... LinuxCompatibility;
{$ELSE}
uses
Linux, ...;
{$ENDIF}
Tendrá que usar compilación condicional para la cláusula uses de todos
modos porque las unidades para Windows y para Linux serán diferentes, así
el único "cargo adicional" para el separador de caminos es agregar
LinuxCompatibility a la cláusula uses.
Letras de unidades de disco
===========================
Windows usa letras seguidas de dos puntos (":") para designar las uni-
dades de disco. Por ejemplo generalmente "A:" se refiera a la disquetera,
"C:" se refiere a la primera partición del primer disco duro y "D:" se
puede referir a otra partición del mismo u otro disco duro, a una unidad
de red o al CD-ROM por ejemplo.
No hay letras de unidades en Linux. Los dispositivos son mapeados como
ficheros o directorios bajo un único directorio raíz ("/"). Por ejemplo,
la primera unidad de diskettes se referencia por /dev/fd0 y usualmente
se monta como /mnt/floppy usando este comando:
mount -t msdos /dev/fd0 /mnt/floppy
Por ejemplo, lo que en Windows es A:\DIR\FILENAME.EXT en Linux sería
/mnt/floppy/DIR/FILENAME.EXT si está seguro que /dev/fd0 está montado en
/mnt/floppy dado que el usuario puede montarlo usando un nombre diferente
cada vez, así que debería siempre solicitar ubicaciones al usuario. Si su
programa hace una copia de seguridad por ejemplo, en Linux permita al
usuario elegir un directorio en vez de una unidad de disco... ¡Y no
muestre "A:\" como predeterminado! :)
Si usa los diálogos estándar y los controles Win32 para obtener nombres
de ficheros o ubicaciones entonces no debería tener mucho problema, pero
si de algún modo ha separado las unidades de los caminos, entonces
debería usar compilación condicional para ignorar las unidades en Linux y
eso es todo!
________________________________________________________________________
4. ¿QUE SIGUE?
La primera edición del Pascal Newsletter aparecerá probablemente la
semana entrante. Continuaremos con la introducción a Object Pascal,
seguiremos discutiendo las cuestiones de portabilidad y también habrá
artículos acerca de la programación en Delphi para Windows. ¡Nos vemos!
________________________________________________________________________
Página principal: http://www.latiumsoftware.com/es/pascal/index.php
Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/
Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com
Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com
Para reportar problemas con la suscripción: eds2004 @ latiumsoftware.com
________________________________________________________________________
Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase.
Su uso implica la aceptación de nuestros términos de licencia y de la
ausencia de garantía que puedes leer en nuestro sitio web. Allí también
encontrarás una nota sobre marcas registradas. Te animamos a que redis-
tribuyas este boletín, siempre y cuando lo hagas en forma completa
(incluyendo la información de copyright), sin modificaciones y de manera
gratuita. Los artículos son copyright de sus respectivos autores y se
reproducen aquí con el permiso de los mismos.
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/es/index.php
Copyright (c) 2000 por Ernesto De Spirito. Todos los derechos reservados
________________________________________________________________________
|