Tutorial Delphi
Tutorial Delphi
Capítulo 0. Introducción
Capítulo 1. La aplicación - El "archivo de Proyecto de Delphi"
Capítulo 2. Nuestro primer programa de Delphi - Una Forma
Capítulo 3. Objetos, Unidades, Formas y Two-Way Tools
Capítulo 4. Usando Delphi Wizards - Primera Aplicación
Capítulo 5. Acceso de Bases de Datos en Delphi
Capítulo 6. Escribiendo una Aplicacion de Base de Datos
o Capítulo 6.1. Cómo funciona Cliente/Servidor en 20 minutos
o Capítulo 6.2. Continuando con nuestro catálogo. Manejando parámetros [Fuentes!]
o Capítulo 6.3. Los módulos de datos (En Construcción)
o Capítulo 6.4. Migrando nuestra aplicación a módulos de datos [Fuentes!]
o Capítulo 6.5. MIDAS y nuevos paradigmas: Objetos de negocios
o Capítulo 6.6. Migrando nuestra aplicación a MIDAS [Fuentes!]
Capítulo 7. Componentes y Paquetes
Capítulo 7.1. Dos Modos Fáciles de Escribir un Componente [Fuentes!] [Fuentes (ActiveX)!]
Capítulo 8. Open Tools API
o Capítulo 8.1 Creando una pequeña ayuda para el desarrollador.
Capítulo 9. Teoría de Interfases
o Capítulo 9.1. Interfaces COM en Delphi (En Construcción)
o Capítulo 9.2. Controlando Word y Outlook 98 desde Delphi
o Capítulo 9.3. Interfaces CORBA en Delphi
o Capítulo 9.4. Mezclando Lenguajes y Plataformas con CORBA: Cómo Platicar con
programas en Java [Fuentes!]
Capítulo 10. Delphi y el Internet
o Capítulo 10.1. Teoría de Comunicaciones en Internet
o Capítulo 10.2. Programando Sockets en Delphi [Fuentes!]
o Capítulo 10.3. Delphi como Cliente de Internet
o Capítulo 10.4. Programando un lector de e-mail (POP3/SMTP) (En construcción)
o Capítulo 10.5. Delphi como auxiliar de servidor de Web (En construcción)
o Capítulo 10.6. Haciendo un servidor de CGI para Web
Capítulo 11. Utilizando el Sistema Operativo (En Construcción)
o Capítulo 11.1. Hilos de Ejecución [Fuentes!]
o Capítulo 11.2. Manejo de Ventanas en Bajo Nivel
o Capítulo 11.3. Utilizando el Registro de Windows
o Capítulo 11.4. Manejo de Procesos en Windows
Artículo: Consideraciones de Diseño en Delphi (En Construcción)
Encuesta
Su opinión es importante. Por favor llene esta encuesta. Su información meserá enviada via e-mail
privado y no será compartida con nadie. Utilizaré las respuestas que reciba para mejorar mi
página.
Capítulo 0. Introducción
Este curso esta enfocado para programadores de computación. No es necesario tener experiencia
con ningun lenguaje en particular, pero es importante haber programado antes, ya que los
conceptos básicos de programación solo se mencionan una vez. Los tipos de letra utilizados
cambiarán mientras cambio el diseño de mi página poco a poco.
El código en Delphi, dentro de las posibilidades de mis utilerías, esta escrito en
HTML con los fonts y sintaxis de colores, de una manera análoga a lo que
encontrará en el editor de Delphi. Muchas gracias a Gerald Nunn et al, autores de
las ayudas de Diseño de Delphi "GExperts", por el convertidor de PAS a HTML con
sintaxis de colores. Este es un ejemplo de código:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
begin
{Estos son comentarios}
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Una nota interesante es que el IDE esta hecho en Delphi, y utiliza las mismas librerias que usted
utiliza para compilar su programa. Esto quiere decir dos cosas:
Primero, que todo lo que puede hacer el IDE es posible para usted (lo cual es notorio
contraste con lenguajes como Visual Basic).
Segundo, que el IDE esta abierto, lo cual quiere decir que usted puede no solo extender la
libreria para que Delphi utilice los "componentes" diseñados por usted, sino que además
puede extender el IDE para hacer "expertos" y ayudas de programación.
Un programa es una serie de instrucciones que son ejecutadas por la computadora en secuencia.
En los viejos tiempos de BASIC, la secuencia estaba especificada por números de linea. En los
demás lenguajes la secuencia está simplemente especificada por las líneas del programa, que van
una detrás de otra (a menos que el programa se haya ido de fiesta anoche). En DOS, cuando el
programa ejecuta la ultima línea, el programa termina.
El "programa principal" de Delphi es un archivo de texto ASCII con extensión .DPR. Ésta extensión
quiere decir Delphi PRoject (proyecto de Delphi).
Para cada una de las ventanas que usted diseña en el IDE, Delphi crea una "unidad". Una unidad
es un archivo individual (tambien de texto ASCII) que representa en general a un objeto, o a una
agrupación logica de funciones. En el caso de los "objetos" que son formas, Delphi también crea
un archivo "DFM" (Delphi Form) para guardar la apariencia del diseño de las mismas (las formas
son simplemente archivos de recursos en un formato especial que solo funciona en Delphi - ver
"archivos de recursos (RES)").
El siguiente ejemplo muestra el archivo DPR que se crea cuando comienza usted
Delphi. En general usted puede crear programas muy complejos sin jamás
modificar el archivo DPR (tambien llamado archivo de proyecto). Pero es
importante saber como funciona. El archivo de Proyecto "Project1.Dpr" tiene la
siguiente apariencia:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Analicemos este archivo. La sentencia "program project1" nos dice que el programa se llama
project1. El nombre de programa debe ser el mismo que el nombre del archivo (sin extensión).
La seccion USES nos dice que archivos son usados. Forms es la librería de Delphi que contiene los
comandos para crear y manipular ventanas (que en Delphi se llaman Formas). El otro renglón en
la sección "uses" nos dice que la unidad llamada "Unit1" se encuentra en el archivo "unit1.pas", y
Delphi nos hace un comentario {} que dice que la forma en esta unidad se llama Form1.
$R es una "directiva del compilador". Una directiva es un comando para el compilador, no para el
IDE. Cuando el compilador encuentra la directiva {$R *.RES} un archivo de "recursos" con el
mismo nombre del programa (pero con extension RES) es encadenado junto con el programa.
Una vez que Delphi ha especificado todo lo que va a usar el proyecto, el programa inicializa la
aplicación (Application.Initialize), crea la forma Form1 a partir de la definición de objeto "TForm1"
(veremos el significado de esto mas adelante) usando "CreateForm", y la aplicación "corre"
(Application.Run). Cualquier forma creada con CreateForm está "viva" (aunque podria ser
invisible) hasta que el Application.Run termina. Despues de esto (normalmente cuando "Run"
recibe el mensaje "Terminate") el programa termina.
¿Porqué Application.Run?
En el siguiente capítulo veremos como Delphi nos facilita la construccion de una aplicación, y
escribiremos una sola línea de código para ver el poder que nos proporciona Delphi para el
desarrollo de aplicaciones.
Capitulo 2. Nuestro primer programa de Delphi - Una Forma
¿Listos para escribir nuestro primer programa? Comencemos entonces. Como mencioné
anteriormente, Project1 es un proyecto vacío que simplemente contiene una forma vacía.
La parte de arriba contiene el menú principal de Delphi, y contiene botones para hacer cosas con
el proyecto (abrir, salvar, compilar) en la parte izquierda. La parte derecha contiene la "paleta de
componentes" que es un menú de botones con todos los componentes que podemos poner en la
forma, en distintas páginas.
Nota: Mi paleta contiene add-ons y varios componentes que pruebo. En éste directorio de Delphi
usted encontrará los sitios donde puede usted evaluar y comprar muchísimos
add-ons para Delphi. Estos add-ons pueden hacer que mi Delphi no sea
exactamente igual al suyo.
Lo que queremos hacer para este primer ejemplo es simplemente crear un botón que, cuando se
presione, de un mensaje de bienvenida (como en la imagen anterior). Para esto hagamos lo
siguiente:
Teclee lo siguiente:
Ahora, presione el botón "Run" de menú de botones de Delphi. Si todo sale bien, Delphi compilará
y encadenará su programa. Despues esconderá las ventanas de diseño y desplegará "[Running]"
en su barra de título.
Felicidades! Acaba usted de crear, compilar y encadenar un programa de Windows. Veamos lo que
puede hacer su programa "de una linea"
Su programa tiene una forma. La forma se puede maximizar, minimizar, cambiar de tamaño y de
posición en exactamente la misma manera que cualquier programa de Windows - sin una linea de
codigo de su parte (en el futuro, cuando usted sea un super-experto y empieze a manejar
"mensajes de windows", en cualquier lenguaje, se dara cuenta de cuanto trabajo Delphi le ahorró).
Su forma tambien tiene un botón. Presiónelo. Verá un mensaje que dice que "esto está Padrísimo"
con un botón de Ok.
Para cerrar el programa, presione el ícono de la "X" en la parte superior derecha, como en
cualquier otro programa de Windows. Su programa regresara al diseñador.
Capítulo 3. Objetos, Formas, Unidades y Two-Way Tools
Este capítulo nos ayudará a entender como funciona la POO (programación orientada a objetos),
cómo funcionan las formas y unidades en Delphi, y cómo maneja delphi la sincronía entre código y
diseño.
Objetos
La programación orientada a objetos ya no es una "moda". Ahora la programación por objetos esta
cambiando la forma en que vemos los problemas. Este no es el momento ni el lugar para hablar
de la teoría de objetos (un buen libro de tecnologia de objetos de Grady Booch dará una mucha
mejor explicación que este curso acerca de lo que es un objeto en programación). Pero es
importante saber las bases de lo que es un objeto para saber cómo se aplica a Delphi.
¿Cómo podemos modelar un objeto del mundo real con objetos "de computadora"? Veamos un
ejemplo:
Supongamos que estamos haciendo un juego acerca de un Acuario. Para este juego queremos
diseñar un Bonito Delfín que va a brincar con el aro y jugar con la pelota, pero queremos en el
futuro extender el juego para cualquier tipo de animal marino porque va a ser una simulación bien
picuda. Tal como en el mundo real, necesitamos comenzar con un objeto llamado "pescado"
(Para los amantes de la biologia: Ya se que el delfín es un mamífero y no un
pescado, pero éste es un ejemplo). El objeto pescado tiene sus datos, como son
alto, largo, peso y color. Pero el pescado tambien puede hacer otras cosas, como
por ejemplo nadar y sumergirse. Entonces primero hacemos nuestro objeto
pescado, que tiene la siguiente forma:
TPescado = class(TObject)
Largo : Float; // El largo del pescado, en centimetros
Alto : Float; // La altura del pescado, en centimetros
Ancho : Float; // El ancho del pescado, en centimetros
Peso : Float; // Cuanto pesa el pescado, en gramos
public
procedure Nadar(AdondeXY, AdondeXZ, AdondeYZ : TPoint);
procedure Sumergirse(Profundidad : Float);
end;
Este código le dice a la computadora: TPescado es una clase que hereda de TObject (o "Un
Pescado es un Objeto") . Tiene los campos Largo, Alto, Ancho y Peso, que son números de punto
flotante. Sus métodos públicos (lo que todo mundo sabe que un pez puede hacer) son Nadar y
Sumergirse.
Si esto parece complejo, por favor sigan leyendo. Muy pronto todo quedará claro.
Ahora el Delfín. He decidido para este ejemplo que hay dos clases de delfines, los
entrenados y los salvajes (no entrenados). Asi que comencemos con el delfin
entrenado:
TDelfin = class(TPescado)
LargodeNariz : Float; // El largo de la nariz del delfin
public
procedure TomarAire;
procedure HacerRuido( Decibeles : Integer );
end;
Ahora bien, este codigo le dice a la computadora: El TDelfin es una clase que hereda de TPescado
(o "Un Delfin es un Pescado"). Esto quiere decir que un Delfin puede hacer todo lo que un
pescado puede hacer (tiene largo, alto, ancho, peso y además puede nadar y
sumergirse). Entonces yo ya terminé mi delfin, y no tuve que implementar de
nuevo las funciones para nadar y sumergirse, que el pescado (y ahora tambien el
delfin) puede hacer. Ahora vamos a entrar en la materia del jueguito, el delfin
entrenado:
TDelfinEntrenado = class(TDelfin)
public
procedure JugarConPelota( Segundos : LongInt; );
procedure BrincarElAro( Circunferencia : Integer );
end;
El TDelfinEntrenado es una clase que hereda no de TPescado, sino de TDelfin (o "Un delfín
entrenado sigue siendo un delfin, pero..."). Al igual que la vez anterior, un delfín entrenado puede
hacer todo lo que un delfin puede hacer, pero además puede jugar con pelota y brincar el aro.
¿Porqué hacer tres objetos nada más para representar un delfín? Supongamos
que ahora quiero hacer un tiburón...
TTiburon = class(TPescado);
NumeroDeDientes : LongInt;
Bocon : Booolean;
ComeHombres : Boolean;
public
procedure Enojarse;
procedure ComerPersona( Quien : TPersona);
procedure EspantarVisitantes;
end;
TObject
+----- TPescado
|
+----- Delfin
| |
| +----- Delfin Entrenado
|
+----- Tiburon
La jerarquía de objetos es una especie de "árbol genealógico" que nos dice qué objetos se
"derivan" de otros objetos. Como conceptualizamos esta jerarquía? Es de hecho bastante sencillo
una vez que nos acostumbramos al árbol. Por ejemplo, supongamos que queremos saber que
interfaces soporta el "Delfín Entrenado". Leemos todos los "padres" de la siguiente manera: "Un
TDelfin ES UN TPescado, que ES UN TObjeto". Esto quiere decir que un objeto de tipo TDelfin
soporta todas las interfaces que el objeto Tpescado y el objeto TObject.
Los lectores mas perceptivos han notado que Delphi utiliza una T para especificar que algo es un
objeto. De hecho, aqui Delphi se parece mucho a C: Un TObjeto es la definición de la clase del
objeto, mientras Objeto1 es una variable de tipo TObjeto. Asi, TForm es la clase, pero la variable
que usted usa para sus formas se llama Form1. La variable Button1 es un "puntero" a la instancia
de clase TButton.
Supongamos que usted tiene el siguiente código en algun lugar de una forma con
un boton (como la que acabamos de escribir). No lo intente, esto es teoría:
var
ElBoton : TButton;
begin
ElBoton := Button1;
Button1.Free;
ElBoton.Caption := 'ESTO VA A TRONAR!!!';
end;
La definición de variable nos dice que "ElBoton" es una variable que apunta a un objeto de clase
TBoton. Despues hacemos que ElBoton "apunte" (recordemos que a final de cuentas son punteros)
al Button1 de la forma. Después liberamos el Button1. Ahora tratamos de cambiar el texto del
botón. Obtendremos una de esas horribles "Fallas de protección general". ¿Por qué?
Button1 = $01A73F
Esto lo menciono porque en cuanto un nuevo programador de Delphi trata de manipular objetos,
tarde o temprano se encuentra con un problema de este tipo. Es un error común para el
programador de objetos tanto en C como en Pascal, y es la escencia de la "Falla de Protección
General", o GPF. El motivo de esta falla es básicamente que estas tratando de utilizar memoria
que ya no existe en tu aplicación.
Formas y Unidades
Las formas en Delphi son objetos de tipo "TForm". Delphi viene con una tabla con la "jerarquía" de
objetos de la libreria VCL. El programa que diseñamos en el capitulo anterior tenía un objeto tipo
"TForm". Este objeto es la ventana estándar de Windows para Delphi. Cuando hicimos nuestro
programa con una línea de codigo, usted no tuvo que decirle a TForm como minimizar, maximizar,
restaurar, mover o cambiar la ventana de tamaño. Usted sólo le dijo a la computadora: Mi TForm1
es un objeto tipo TForm. Delphi sabe que TForm tiene la capacidad de hacer todo lo anterior (y
más). Entonces el comportamiento de todas las formas que usted cree a base de TForm es el
mismo. Usted no tuvo que hacer nada para utilizar la magia de TForm - ni siquiera fue necesario
saber que TForm era un objeto.
(Delphi tambien le ofrece poder. Si usted quiere hacer cosas muuuy raras, puede usted evitar
TForm y utilizar TCustomForm, que es una clase abstracta para que usted haga su formas con
comportamientos muy extraños, si se avienta - pero primero hay que aprender más).
Cada forma es guardada en un par de archivos: El archivo PAS (donde escribimos el código) y el
archivo DFM (donde diseñamos la forma).
El Archivo PAS
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
end.
Examinemos la interface de este programa: La primera sección nos dice que esta unidad utiliza las
librerias Windows, Messages, SysUtils, etc. Despues viene la definición de typos (type), donde
Delphi ha definido por nosotros la TForm1 que ha creado (en Delphi, las cosas no solo "aparecen"
como en otros lenguajes. Delphi es un lenguaje decente y siempre pone la
representación de todo el diseño en archivos legibles y modificables con el
editor). Object Pascal es un lenguaje "strong-typed", lo cual quiere decir que
cualquier variable, objeto o rutina debe ser declarado antes de ser escrito. La
declaración de la forma tambien declara el Botón que le pusimos. Si usted se
pone a poner mas botones a la forma, verá que Delphi añade secciones Button2,
Button3 a la unidad de la siguiente manera:
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
También podemos ver que Delphi definió el procedimiento Button1Click para cuando el usuario
apriete el botón, y este procedimiento pasará el parametro (Sender) lo cual le dirá a nuestra rutina
qué objeto fue el que ejecutó el evento (normalmente Button1, pero esto nos da el poder de
compartir un "procedimiento manejador de eventos" para mas de un objeto - veremos como hacer
esto mas tarde).
Si usted quiere añadir sus propias rutinas manualmente, Delphi ya nos pre-escribio una seccion
privada ( private) y publica ( public ) para que podamos añadir funciones manualmente (existen
mas directivas, como "protected" o "automated", pero las mas usadas son estas dos, por lo cual
Delphi las define por nosotros. Delphi trata de ayudar sin estropear nuestro código con mucha
basura que no vamos a usar). Las funciones privadas solo son visibles para TForm1, mientras que
las funciones públicas son visibles para cualquier objeto que use TForm1.
Ahora la última seccion. Si usted es muy perceptivo habra notado que he hablado de TForm1 (la
clase) pero la variable que inicializaría la clase (de la cual hablamos con anterioridad) no ha sido
definida. Bueno, pues la última sección del área de la interface lo define:
var
Form1: TForm1;
Aquí le decimos a Delphi que la variable Form1 es una variable de tipo TForm1. ¿Y quién inicializa
la variable Form1? Bueno, presione Control-F12 (View Unit=Ver Unidad) y seleccione Project1. Una
de las líneas en el archivo de proyecto dice:
Application.CreateForm(TForm1, Form1));
Borland ha perfeccionado una tecnología llamada "Two-way tools". Esto quiere decir que su
programa esta escrito en un archivo de texto que usted puede editar inmediatamente, pero al
mismo tiempo está representado en los diferentes diseñadores de Delphi. Cada vez que usted
añade un objeto, modifica una propiedad o agrega un evento, el IDE "sabe" donde añadir ese
objeto, propiedad o evento en la interface, y muchas veces escribe código de inicializacion con
bloques "begin-end ", listo para que usted escriba su código y siga su camino. Los "two way tools"
de Borland son los mas estables y rápidos del mercado, y son los unicos que le permiten editar la
forma en el diseñador o directamente como texto.
En el capítulo anterior, cuando usted hizo doble click en el botón, los "two-way-tools" de Delphi
escribieron el código para el evento Click del botón y lo asignaron al mismo.
Éste es el texto de nuestra forma. Como verá, todas las propiedades "publicadas" son guardadas
aquí. Cada vez que usted cambia una propiedad en el "inspector de objetos", este archivo es
modificado. Por ejemplo, cuando hicimos doble-click en el botón para escribir el código de OnClick,
Delphi modificó el archivo PAS para declarar la rutina "Button1Click", pero ademas añadió un
renglon a esta definición de la forma ( OnClick = Button1Click), diciendole al compilador que
el evento OnClick del Boton 1 esta enlazado al procedimiento Button1Click. Cuando modificamos
el "Caption" del objeto botón, Delphi lo modifico en este archivo. Si quiere Ud. puede modificar el
texto "&Botoncito" para que diga "&Botonzote".
Delphi ha escrito bastante código por nosotros, no cree? Pero aún asi, nos permite ver y modificar
lo que queramos. En general usted no necesita modificar la forma de esta manera. Pero si lo
quiere intentar, tenga cuidado. Delphi puede perder "el hilo" de como esta guardada la forma si
usted modifica las secciones con los nombres o tipos de los objetos. Siempre mantenga un
respaldo de su DFM antes de modificar la forma como texto.
Para salir de la vista de la forma "Como texto", presione Alt-F12. Delphi. Delphi leerá la forma de
nuevo (para interpretar sus cambios) y regresara al diseñador.
Object Repository
El Object Repository es un directorio donde usted puede poner codigo fuente basico y
componentes que usted utiliza una y otra vez, como formas, su "aplicacion estandar" y cosas asi.
Cuando usted selecciona File-New, el Code Repository muestra un una pagina con todas las
opciones de Wizards y lo que contiene el repositorio para que usted eliga. Usted puede no solo
copiar el codigo a su proyecto, sino "heredar" y "usar".
Heredar una forma (inherit) es una manera muy util de, por ejemplo, hacer que las formas de su
aplicacion tengan una apariencia fija. Si usted siempre tiene un Toolbar vacio y su forma siempre
tiene el caption rojo, por ejemplo, usted puede heredar de esa forma para todas las formas de su
aplicacion, y Delphi creara las formas basadas en su forma en vez de basarlas en TForm. Cualquier
objeto puede ser heredado. Simplemente "usar" quiere decir que no vamos a mantener una copia
del objeto en el repositorio y tampoco lo vamos a utilizar.
Wizards y Add-Ins
Un wizard es un DLL (o DPL) de Delphi que nos ayuda a escribir codigo. Los Wizards pueden ser
desde muy sencillos (wizard para "New Dialog"), hasta muy complejo codigo que cambia la
totalidad del IDE (como Coderush, o GExperts). Cuando son muy complejos y cambian el IDE se
llaman "Add-Ins" o IDE Enhancements.
Porque el rango tan amplio de complejidad y son lo mismo? Aunque eso es para otro capitulo, por
ahora le puedo decir que Delphi esta hecho en Delphi (y la libreria sigue siendo el VCL), asi que
cuando usted hace un Add-In, todos los objetos del IDE de Delphi estan disponibles para que usted
los manipule. El IDE de Delphi tiene ademas interfaces que hacen estas modificaciones faciles,
llamadas Open Tools API. Eso quiere decir que su Add-In puede manipular el menu añadiendo
items, cajas de dialogo, modificando las ventanas del IDE e incluso reemplazando tareas de
Delphi! Este es el poder de un ambiente totalmente abierto a que usted juege con el. Coderush es
el ejemplo mas extremo en el mercado (hasta ahora) de lo que se puede hacer con este API.
Comencemos por algo facil, utilizar uno de los wizards que vienen con Delphi. Para esto no
tenemos que saber nada...! Simplemente, seleccione File-New... (No use File-New application
porque esto creara una aplicacion vacia tal como la que ve cuando comienza Delphi) - la ventana
del repositorio de objetos desplegara las opciones de los nuevos elementos que puede crear:
Delphi tiene templates categorizadas por pagina:
New - Esta pagina contiene templates para crear nuevos elementos como Aplicaciones,
componentes, modulos de datos, DLLs, Formas, Paquetes de componentes (DPL), Modulos
de datos remotos (MIDAS/DCOM/Multi-tier), reportes, threads y servidor de Web.
ActiveX - Con esta pagina puede usted crear ActiveForms (que funcionan en la pantalla
muy parecido a los applets de Java), Control de ActiveX (OCX para usar en Delphi, C++,
Visual Basic o VBScript en paginas de web), Automation Object (objeto COM basico sin
interface de usuario, pero con conteo de referencia integrado), Pagina de propiedades
ActiveX, y Type Library.
Su Proyecto - El proyecto que se encuentra abierto tambien aparece como pagina para
que usted pueda heredar rapidamente unas formas basadas en otras. Haremos esto mas
tarde.
Forms - Varias formas y reportes diferentes (About, list box, labels, etc).
Dialogs - Varios dialogos, asi como un Wizard que lleva paso a paso por la creacion de un
dialogo.
Projects - Wizard para aplicaciones, Aplicacion MDI, SDI y aplicacion compatible con el
logo de Win95 (esta ultima te permite hacer una aplicacion basica con los "esqueletos" de
todo lo que Microsoft pide para que te ganes un logo de "Diseñado para Windows 95".
Data Modules- Los modulos de datos que haga usted pueden ir aqui. En la seccion de
bases de datos veremos como funcionan los modulos de datos.
Business - Esta pagina tiene un wizard para hacer una forma con campos (lista para
ejecutar) a partir de una base de datos, un "cubo de decisiones", un wizard para hacer
reportes, y un wizard para graficacion.
Cuando use Delphi para producir sus aplicaciones, podra usar Tools-Repository del menu de Delphi
para hacer mas paginas y organizar sus wizards o los modulos de datos que haga. Ademas,
usando Tools-Environment options - Shared repository directory podra usted cambiar el directorio
para que este en su directorio de datos, o en algun lugar de la red (asegurese de copiar los
archivos de C:\Program Files\Borland\Delphi 3\ObjRepos, o equivalente, al nuevo directorio para
que los wizards sigan funcionando).
Ahora que sabemos lo que es el repositorio de objetos, por que no usamos nuestro primer Wizard?
Y de paso creamos nuestra primera aplicacion de a deveras...!
A continuacion, nos presentara con una interface para añadir botones dependiendo en los
comandos que seleccionamos:
Utilice la parte izquierda para seleccionar la pagina y la derecha para cambiar los botones. Los
botones Insert, Remove y Space agregan, eliminan y ponen espacio entre los botones. En la parte
superior vera usted la barra de botones tal y como se va a ver. El pequeño triangulo en esta barra
representa donde se insertara el boton y puede ser movido para que usted ponga botones en
medio si olvido alguno.
Create MDI Application - Si elige esta opcion, su aplicacion sera estilo MDI (este estilo no
debe ser usado en Windows 95 segun Microsoft, pero paradojicamente todas las
aplicaciones de MS-Office 95 y 97 lo utilizan). El estilo MDI muestra una ventana "maestra"
para la aplicacion y documentos adentro de esta (su nombre proviene de las siglas de
Multiple Document Interface, en alusion a esto). Si no elige esta opcion, su aplicacion sera
SDI (single document interface).
Create a Status Line - Si elige esta opcion, Delphi creara una barra de status y codigo
para que cada vez que el cursor se ponga sobre un boton el texto de "hint" se muestre en
la barra de status.
Enable Hints - Con esta opcion, Delphi hara que todos los objetos de la aplicacion tengan
el desplegado de Hints automatico habilitado (enabled).
Seleccione Create Status line y Enable hints. Delphi se ira a trabajar y dejara su programa listo
para ejecutar por primera vez. Felicidades! Acaba usted de crear su primera aplicacion en Delphi.
Todavia no hace nada, pero haremos un mini-editor de textos, nada mas para ver que tan facil es.
Nuestra Primera Aplicacion en Delphi
Esta es la apariencia de nuestra primera aplicacion en Delphi, la cual acabamos de crear utilizando
el Wizard de Aplicacion nueva dentro del Object Repository. Utilizando el Object Inspector, cambie
el Caption a "Mi Editorcito". Si usted quiere, puede ejecutar este programa tal como esta. Presione
el boton de "Run" en el IDE y pruebelo. Trate de presionar los botones y note que ciertos botones
despliegan dialogos (Open, Save)! Pronto veremos como se hace esto. Seleccione File-Exit para
salir de su aplicacion de ejemplo y note como Delphi ya ha escrito codigo para salir del programa
desde una opcion del menu.
Como se dio cuenta al ejecutarla, esta aplicacion tiene ciertos componentes que no se mostraran
al usuario en tiempo de ejecucion (Delphi los ha agrupado en medio de la forma sin alinearlos). El
VCL de Delphi se divide en dos clases de componentes: Componentes Visuales y componentes
No Visuales. Los componentes no visuales son representados por Delphi como iconos con
sombra menos "obscura" en el diseñador, y no se pueden contener dentro de un panel o barra de
botones (porque como no se ven, no tiene caso ponerlos "adentro" de un componente visual).
Los componentes no visuales en esta forma son el Menu principal, el dialogo "Open", el dialogo
"Save", el Dialogo "Print" y el dialogo "Print Setup". Estos objetos del VCL son los dialogos
estandard de Windows encapsulados en un objeto de Delphi (o sea que se veran en español,
ingles, aleman, etc., dependiendo del idioma de Windows en la maquina "cliente").
Para editar el MainMenu, haga doble-click en el icono del menu (componente no visual) y vera un
editor de menus.
Este evento, que esta enlazado al evento "Onclick" del item del menu FileOpenItem, nos muestra
que para accesar uno de estos objetos "invisibles" solo tenemos que mencionarlo por nombre y
llamar uno de sus "metodos" (procedimientos o funciones de los objetos) con un punto en medio,
en el formato Objeto.Metodo. (de la misma manera en que C++ utiliza el formato Objeto-
>Metodo ). En el caso de todos los Dialogs estandar de Delphi que se encuentran en la pagina
"Dialogs" de la paleta de componentes (Open, Save, Print, Print Setup, Find, etc), el metodo para
mostrar el dialogo al usuario se llama Execute. Execute es una funcion que devuelve un valor
Boolean (logico). Si Execute devuelve True, quiere decir que el usuario completo el dialogo con
exito y salio con OK. Si Execute devuelve False, quiere decir que el usuario no completo el dialogo.
Es por esto que Delphi ha encerrado la seccion donde dice "Añada codigo para abrir..." dentro del
If-Then. De este modo el archivo solo abrira cuando el usuario seleccione OK (y Cancel funcionara
tal como esperamos como usuarios, no haciendo nada). Si se asoma al codigo de Save, print, etc.,
vera que es el mismo asunto.
Ahora que ya probamos nuestro programa comencemos a hacer cosas para que de hecho haga
algo. Dijimos que vamos a crear un editor de textos. Pues comencemos. En la paleta de
componentes, vaya a Win32 y seleccione el componente RichEdit. Ponga uno de estos en medio
de la forma. En el Object inspector, cambie la propiedad Align a alClient.
Nota: Delphi tiene nombres para diferentes items asociados con un tipo
de propiedad. Estos se llaman "enumeraciones" o "enums". En este caso,
la propiedad Align del objeto TControl (y sus derivados, que incluyen a
TRichEdit) es de tipo TAlignment, que puede ser alNone, alTop,
alBottom, alLeft, alRight o alClient. Internamente durante compilacion,
Delphi compila esto en un set numerico. C tiene capacidades parecidas a
traves de la directiva typedef enum, pero en Delphi es mucho mas
simple (por supuesto). Internamente, las imagenes creadas por Delphi con
este metodo en memoria son identicas a las de C y por ende, es igual de
rapido.
Veremos que el RichEdit contiene el texto "RichEdit1". Esto como que no va en un programa, asi
que vamos a borrarlo. Haga doble click en la propiedad "Lines" y vera una ventanita para editar
textos. Elimine el texto.
Ahora vamos a escribir un poco de codigo, asi que truenese los dedos y agarrese del teclado
(como yo)
Vamos de nuevo a nuestro codigo. Primero necesitamos permitir al usuario grabar el texto que
escriba en el RichEdit. Asi que vayamos a File-Open utilizando el menu dentro del
diseñador. Delphi nos mostrara el codigo dentro de esa seccion. En el codigo,
escriba lo siguiente:
Fue solo una linea... Pero una linea bastante poderosa. Vamos a probarla primero y despues
explicare. Ejecute su programa. Ahora, antes de hacer nada, ejecute WordPad (el editorcito que
viene con Windows). Escriba algo bonito, con Fonts y cosas. Ahora grabelo a disco, seleccionando
el formato RTF. Corra nuestro programa (que ahora puede leer un archivo) y ejecutelo. Cargue el
mismo archivo. Ahora compare:
Heeey... No esta mal para una linea de codigo, verdad? Nuestro programa, aunque todavia no
tiene botones para edicion especifica de fonts y colores, soporta los mismos. Tambien, si se da
cuenta, soporta "word wrapping". Felicidades! Acaba usted de terminar de hacer la parte mas
pesada de los editores de Windows 3.1, el soporte interno de Fonts.
Nota: Paquetes son un tipo de DLLs especifico para Delphi y C++ Builder
que son capaces de pasar objetos en vez de solo tipos basicos (usan la
extension DPL y se encuentran en Windows\System32). Los archivos son
compartidos y cada objeto esta implementado en un paquete. Usted tiene
que poner en el paquete de instalacion los archivos DPL en los que los
objetos con los que programo se encuentran, de preferencia en un
"paquete de web" (CAB) diferente. En el servidor de Borland hay un
paquete CAB con solo los DPLs al cual usted puede hacer referencia en las
dependencias de su propio CAB. De este modo el navegador solo baja el
paquete con los objetos de Delphi una vez, y las veces subsiguientes baja
unicamente 32K. Tambien hay un CAB por separado para la Database
Engine que solo se usa si usted usa tablas o queries.
RichEdit interpreta esto como los diferentes colores y tipos de letra especificos. Imaginese cuanto
tiempo nos han ahorrado implementando todo esto! Un poco de credito debe ir a Microsoft, ya que
RichEdit es uno de los controles estandar de Win95 definidos en commctrl32.dll - pero ellos
cambiaron las complejidades de usar el formato RichEdit con las complejidades de mantener
Window Handles y mensajes de Windows a todo momento. Delphi nos da un componentito que
ponemos en la formay punto.
El Objeto TStrings es uno de los objetos internos que mas se utilizan en Delphi. Tiene la capacidad
de grabarse y cargarse del disco con una metodo (LoadFromFile/SaveToFile), de buscar una
cadena dentro de la lista con el metodo IndexOf, de mantener un puntero de memoria por cada
objeto dentro de la cadena, lo cual lo hace ideal para poner descripciones de texto en una lista de
objetos (como por ejemplo una lista de bitmaps con nombres de archivo) en memoria, y
muchisimas cosas mas. Vea la ayuda de TStrings para saber todo lo que puede hacer con listas, y
por ende, todo lo que puede hacer con la propiedad Lines del RichEdit.
Desde los inicios de Delphi, Borland decidió crear una estructura de acceso de datos abierta. Una
buena parte de la librería de clases VCL esta dedicada a trabajar con acceso de datos de una
manera abierta y flexible. Delphi y C++ Builder tienen métodos de acceso a bases de datos únicos
en la industria por su flexibilidad y facilidad de uso.
Gracias al hecho de que todo en Delphi es un objeto, los creadores de Delphi crearon, entre otros,
un componente abstracto llamado "TDataset". Como su nombre lo indica, un dataset es un "set de
datos". Como podra ver, el set de datos abstracto no especifica el origen de la base de datos,
modo de conexión o marca del producto que está en el servidor. Ni siquiera especifica si nuestro
set de datos es una tabla física, un query de SQL o un archivo de texto! De esta manera se
generaron los componentes que heredan de TDataset bajo una base común.
Los componentes TTable y TQuery son los componentes mas comúnmente usados para acceso
básico a bases de datos (pero no los únicos). Estos componentes representan una tabla y un query
de SQL, respectivamente. Para mantener el acceso de datos compatible con los diferentes
productos de Borland, se desarrolló una serie de librerías redistribuibles llamada "Borland
Database Engine" (BDE). Esta librería debe ser instalada en la máquina del cliente para que TTable
y TQuery funcionen correctamente.
El Borland Database Engine es primordialmente una librería para manejar cursores de SQL.
Muchos lenguajes son capaces de operar con un sólo "renglón" de datos de la tabla a la vez, por lo
cual el manejo de los cursores de SQL no es muy satisfactorio. Pero Delphi utiliza el Database
Engine como una "traducción" de cursores de SQL en el formato de Queries y Tablas interno de
Delphi. Ademas, el Database Engine nos permite accesar el origen de los datos (la base de datos
para la cual estamos escribiendo) a través de "drivers" intercambiables de acceso directo, o si asi
deseamos, utilizar ODBC para accesar estos datos.
¿Complicado? No se preocupe, esta imagen cubre todas las posibilidades de acceso de datos,
incluyendo la nueva tecnología MIDAS, asi que no lo tiene que entender en su totalidad.
Usualmente usted solo seleccionará uno de los métodos de acceso a bases de datos (dependiendo
en su software existente y necesidades de conexión), y se olvidará de todos los demás. Cuando
siga las secciones prácticas en los siguientes capítulos, refiérase a este diagrama para "descubrir"
qué modo de acceso de datos estamos utilizando en cada sesión práctica. Esta estructura pone
todo el poder de acceso a bases de datos de muy diferentes tipos en sus manos, en forma de
simples componentes que usted puede poner en sus formas. Al mismo tiempo, le proporciona
suficiente poder para escribir su propia estructura de base de datos si eso es lo que usted quiere,
sin cambiar los componentes en el lado del cliente.
¿Porqué SQL?
Un buen libro de SQL es "SQL for Dummies", por Allen G. Taylor. Ademas
de ser un libro de SQL en general muy facil de entender, el autor utiliza
las herramientas de Delphi! El libro explica SQL en general sin especificar
una versión de base de datos en particular (Yo sospecho que, ya que esta
usando las herramientas de Delphi, esta programando en Interbase). Si
usted cree que le pueda interesar migrar a una base de datos SQL en el
futuro y utiliza Delphi o C++ Builder, programe en Delphi como si ya
tuviera una, utilizando los drivers de SQL local de Paradox, dBase o
MSAccess. Despues le será mas facil migrar su base de datos.
Un Driver de BDE es un componente de software que actua como intermediario entre la librería de
cursores del BDE y los datos Físicos. Delphi viene con drivers para dBase, Paradox e Interbase,
además de un driver para ODBC, que le permite accesar cualquiera de las bases de datos que
ODBC soporta. En ediciones cliente/servidor, Delphi cuenta con drivers "nativos" de SyBase,
Oracle y MS-SQL Server.
Los Aliases le permiten especificar todos aquellos parámetros que hacen a su aplicación depender
de un servidor en particular afuera de su ejecutable, en un archivo de configuración en la maquina
del cliente. El programa de instalacion InstallShield que viene con Delphi Professional y Client
Server (no estoy seguro si viene con Delphi Standard) le permite instalar el BDE con su aplicación
y especificar los aliases necesarios, respetando las configuraciones ya existentes.
Del lado del cliente en Delphi, la paleta de componentes llamada "Data Access" contiene todos los
componentes no visuales que usted puede poner en su aplicación para conectarse a una base de
datos. Los componentes más importantes incluyen:
TSession
Un alias se conecta a una "sesión", representada por el componente TSession. Esta sesión es una
conexión Física a la base de datos. Los programas de Borland pueden compartir sesiones
globalmente, y cada programa puede tener cualquier número de sesiones simultáneas. De este
modo, usted puede compartir una sola conexión (sesión) a la base de datos para economizar
licencias, sin importar cuantos ejecutables este utilizando para accesar los datos, o si las licencias
por conexion no son limitadas en su base de datos, puede utilizar varias sesiones en una sola
aplicacion para programar sus queries con multitarea.
Incluso si usted no pone un componente TSession en su aplicación, Delphi define uno por cada
aplicación, representado por la variable de objeto global "Session".
TDatabase
TDatabase es la representación de una base de datos. Tal como en su servidor de SQL usted
puede crear más de un "contenedor de base de datos", Delphi puede compartir una conexión
(TSession) entre varios contenedores de bases de datos (TDatabase). Tal como en la sesión, si
usted no pone un TDatabase en su aplicación Delphi especifica uno en la variable de objeto global
"Database".
TTable
TQuery
TQuery es la representación de cualquier comando de SQL que regresa un cursor de SQL (ya sea
de tipo SELECT o un "Stored Procedure" - vea TStoredProc). Este cursor de SQL se comporta como
una tabla. Los programadores con experiencia en SQL encontrarán a TQuery muy útil, ya que se
comporta como un TTable (en el sentido de que tiene First, Next, Last, etc), pero al mismo tiempo
es un comando de SQL. El programador de SQL no tiene que hacer "Fetch". Además, cuando el
usuario edita campos asociados a un TQuery, Delphi se encarga de generar los comandos
"UPDATE" y "DELETE" si el resultado del Query es Read/Write (vea TUpdateSQL).
TDataSource
El Datasource es, como su nombre lo indica, una representación del "origen de los datos".
DataSource no requiere el BDE, pero es el componente que "mapea" los componentes visuales de
base de datos con los no visuales. El hacer un componente intermedio es importante porque de
este modo podemos tener varias representaciones de los mismos datos sin tener que "atar" la
representación de los datos en pantalla a la representación "física" de los datos. Un DataSource se
conecta a su vez a cualquier componente tipo Dataset, como TQuery, TTable, TClientDataset, ¡o el
que usted haya creado! DataSource nos permite ser consistentes en la representación de nuestros
datos en pantalla sin preocuparnos de donde vengan los mismos (Tablas, queries, o nuestros
propios metodos).
TUpdateSQL
En SQL-92, existe una limitación en cuanto a los enunciados "Select". Existen muchas
circunstancias en las cuales SQL regresará un cursor de solo lectura, en vez de el cursor de
Lectura/Escritura que nos gustaría obtener. Por ejemplo, si el enunciado SQL especifica una unión
de tablas, SQL nos devolverá un cursor que podemos representar como el resultado de un Query
en Delphi, pero no podremos editar el resultado. Obviamente esta limitación es muy severa si
queremos hacer ciertas "maravillas" con nuestra aplicación (queries editables con referencias en
texto es una de estas cosas).
Aqui es donde UpdateSQL hace su magia. Todos los queries tienen un Edit, Insert y Delete.
UpdateSQL puede ser encadenado a un query cuyo resultado será de lectura unicamente para
decirle a Delphi qué vamos a hacer cuando el usuario edite, inserte y borre un registro. En el Edit,
podemos poner un SQL UPDATE, en Insert podemos poner un SQL INSERT, etcétera. De este
modo nos "brincamos" la limitación de SQL utilizando sentencias específicas, pero lo mantenemos
automatizado gracias a Delphi.
TStoredProc
Aunque TQuery puede ejecutar un stored procedure que devuelva un valor (y tenemos que usar
un TQuery si queremos recuperar ese valor), a veces el stored procedure no devuelve nada.
TQuery espera un valor de regreso, y nos devolvera un error si el servidor no devuelve un valor.
Por este motivo, para esos procedimientos necesitamos un objeto que nos permita ejecutar un
comando SQL sin esperar ninguna respuesta. TStoredProc es ese comando.
Los componentes visuales de base de datos son los que ponemos directamente en la forma para
desplegar información y permitir al usuario editarla. Todos los componentes de este tipo son
llamados "Data-aware", y tienen el prefijo "DB". De este modo, un componente de edición "Edit"
en su versión "Data Aware" es llamado "DBEdit". ComboBox se convierte en "DBComboBox".
Muchos de los componentes visuales (Incluyendo el Grid) tienen una versión "Data Aware".
Además, usted puede crear sus propios componentes data aware.
Ademas, existe un componente llamado "DBNavigator", que es una serie de botones "mapeados"
a los comandos Next, Prior, Last, Edit, Delete, etc., de cualquier DataSource.
Todo esto le permite a usted poner unos cuantos componentes data aware en su forma (junto con
un DBNavigator), conectarlos a un DataSource (que se encontraria conectado a un Dataset
abierto), y correr su programa, y asi de fácil tendria usted una aplicacion básica con acceso a base
de datos, ¡sin tener que escribir una sola línea de codigo! Lo cual nos lleva a nuestro
próximo capitulo, donde haremos una aplicación de base de datos casi sin tener que usar código!
En este capítulo escribiremos una aplicacion pequeña de base de datos. Como necesitamos
asegurar que todos tengamos los mismos datos, comenzaremos por utilizar los datos que vienen
con Delphi, en el alias DbDemos.
Delphi utiliza una librería llamada BDE (Borland Database Engine) para su acceso
a bases de datos. El acceso de datos en Delphi es muy diferente, pero análogo, al
acceso de datos en lenguajes como Clipper, Foxpro y dBase. En estos lenguajes,
usted tiene "áreas" de trabajo. Dentro de esas areas usted puede tener abierta
una tabla, y le puede decir a esa tabla que vaya al principio, al final, o brinque o
retroceda. Bueno, en principio, en Delphi puede usted hacer lo mismo:
En Delphi usted puede tener un sinnúmero de estas "areas", pero como en Delphi todo es un
objeto, obviamente tienen su nombre. Las clases que representan sets de datos navegables
(datasets) son llamadas "TDataset".
Vamos a entrar en materia y comenzar nuestra primera aplicación de base de datos. Utilizaremos
los datos que vienen con Delphi, ya que son los que nos aseguran que todos veamos las mismas
pantallas. Más adelante veremos otros tipos.
TForm1 = class(TForm)
qryClientes: TQuery;
dsClientes: TDataSource;
dbnNavegador: TDBNavigator;
private
{ Private declarations }
public
{ Public declarations }
end;
¿Como le hacemos para que se active el Query? Lo que vamos a hacer es aprovechar al máximo
las capacidades de diseño de Delphi. Primero, necesitamos hacer un Query en SQL que Delphi
pueda utilizar en el alias DbDemos para que Delphi sepa la información de la tabla que podemos
utilizar para diseñar.
Asegúrese de que el número de cuenta sea el correcto (consulte la tabla usando Database
Explorer si tiene dudas) para que su query devuelva un registro. Si su query no devuelve nada,
todavía podrá diseñar (Delphi devuelve la información de los campos sin registros), pero no verá el
registro en tiempo de diseño.
Ahora lo que queremos hacer es diseñar usando todas las ayudas de Delphi, así que, con el Query
todavía cerrado, haga doble-click en el query. A continuación verá una ventana vacía que dice
Form1.qryClientes. Ésta es la lista de registros existentes en el resultado de nuestro query
"Clientes". Presione Control-A (o haga click con el boton derecho del mouse y seleccione "Add
Fields"). Delphi ejecuta el Query internamente y nos proporciona la lista de las "columnas"
(campos) del resultado del Query. Todos los campos están preseleccionados (Delphi asume que
usted quiere añadir todos los campos a la forma). Así que simplemente presione {Enter} para
aceptar el añadir todos los campos a su definición.
Cambie la propiedad "DisplayName" en todos sus campos y ponga títulos convenientes en cada
uno de ellos (¡y por favor en Español, Chihuahua!)... Una vez que termine de "amasar sus datos",
estará usted listo para diseñar...
Para diseñar su forma, simplemente "jale" cada uno de los campos con el mouse, desde la lista de
campos "Form1.qryClientes", hasta la forma, en el orden que desee que el usuario los escriba, y
en la posición deseada en la forma. Cuando "suelte" cada campo, Delphi creará un componente
TLabel y un DbEdit por usted, listos para recibir los datos adecuados.
"Dijimos que Delphi nos permitía diseñar visualmente.... ¿Donde están los datos?"
Ah, pues es muy sencillo: Nuestro Query todavía no está abierto. Aunque Delphi lo
ejecutó para averiguar la lista de campos necesarios para el diseño, nuestro
query, para propósitos prácticos, está cerrado. Vamos a abrirlo y veamos que
obtenemos:
Nuestros campos han sido activados. El dbNavigator ya nos dá algunas opciones. Pues sí, esa es la
cuenta que especificamos en los parámetros... Y fijate, la Señorita Davis es una muchacha muy
guapa, de Wellesley, Massachusets. Y es programadora, además... Con esa sonrisa, seguro
programa en Delphi.
Si usted quiere, puede correr ahora mismo su programa y hacer cambios, pero solo a ese registro
(porque nuestro query especifica un registro únicamente). Si Delphi no le permite editar mientras
ejecuta, asegúrese de que su Query tiene el RequestLive en "True".
Ahora que sabemos como se hacen las cosas, para experimentar un rato vaya al SQL en su Query
y elimine la sección "WHERE" de la sentencia SELECT. Su SQL quedará como SELECT * FROM
"CLIENTS.DBF" CLIENTES
Recordando todas las precauciones que vimos al principio de nuestro capítulo, sabemos que esta
tabla en particular sólo tiene cinco registros, así que el tiempo de espera del select no será
catastrófico (pero recuerde lo que dije antes; más tarde veremos varias técnicas para manejar
esto). Después, abra su query de nuevo (cambie la propiedad "Active" del Query a "True", y
ejecute el programa.
Finalmente, grabe su ejemplo; lo utilizaremos en otros capítulos para no tener que comenzar de
nuevo cada vez. Cuando le pregunte el nombre de "unit1", teclee forma_clientes. Cuando le
pregunte el nombre para el proyecto, teclee "ManejodeClientes".
En los siguientes subcapítulos de la sección "base de datos", aprenderemos mucho acerca de las
muy diversas maneras en que podemos desarrollar aplicaciones de manejo de datos usando
Delphi. Además nos quitaremos de algunos de los "vicios" que tuve a bien introducir en este
capítulo para hacerlo más sencillo y obtener resultados más rápido, como el mantener las tablas
abiertas en modo de diseño, usar SQL sin sentencias WHERE, etcétera. Siendo Delphi tan
poderoso como lo es para el manejo de bases de datos, es dificil abarcar todo. Pero al menos les
daré una "probadita" de varias de las capacidades de Delphi en cuanto al manejo de información,
que son muchas. A partir de ese punto, usted tendrá las herramientas suficientes para
experimentar y extender sus conocimientos.
Por ahora, dése unas palmaditas en la espalda, porque ya terminó una de las secciones más
largas de el curso! Ha usted hecho bastante, y todavía ni siquiera hablamos del lenguaje en sí, un
área fascinante que veremos más a fondo mientras viajamos por los ejemplos...
Como éste curso es de Delphi y no de SQL, explicaré muy rápido los comandos básicos que se
utilizan en este lenguaje de programación, que en realidad merece un curso propio. Para mayor
velocidad y entendimiento de conceptos que Delphi utiliza para enmascarar los comandos SQL en
sets de datos (dataset), he decidido dividir la sección de comandos entre devuelven un set de
resultado, comandos que no devuelven un set de resultado (recuerden que un set de resultado sin
registros sigue siendo un set de resultado), y comandos para manipular metadatos. Por cierto, un
comando SQL puede estar escrito en varias líneas.
Junto con cada comando incluyo un poquito de su sintaxis básica (varios comandos son muy
complejos) y unos ejemplos para que usted vea en qué clase de situaciones se utilizan los
comandos.
Todos los comandos mencionados en esta sección pueden devolver un set de resultado. Estos
comandos son:
SELECT
Procedimientos ( CREATE PROCEDURE )
Vistas ( CREATE VIEW )
SELECT
Ejemplos
Por ejemplo, para hacer un Select que nos devuelva toda la información en la tabla de clientes
donde el cliente tiene como domicilio la ciudad de México, DF, puede usted escribir:
El comando JOIN sirve para juntar campos de dos entidades. Un uso común en SQL para el
comando JOIN es la llamada "denormalización" de los resultados. Esta denormalización es solo de
los resultados, para que el SQL devuelva un set consistente de resultados (siempre el mismo
número de columnas). por ejemplo, supongamos que usted tiene información de una factura.
Tiene usted el encabezado de la factura y además una serie de renglones con los precios y
descripciones de los artículos comprados. Para devolver información que usted pueda desplegar,
puede usted escribir su SELECT con un JOIN de la siguiente manera:
SELECT
ENC.FACNO,
ENC.NOMBRECLIENTE,
ENC.CIUDAD++ENC.ESTADO AS CIUDADEDO
REN.ITEMNO,
REN.DESCRIPTION,
REN.PRECIO_USD,
FROM FACTURAS ENC
LEFT JOIN FACTURARENGLON REN ON ENC.FACNO = REN.FACNO
WHERE ENC.FACNO = 1
Examine el SQL escrito aquí. En este comando de SQL tenemos dos tablas: Una llamada
FACTURAS que estoy "abreviando" a ENC (por encabezado), y la otra llamada FACTURARENGLON,
que he abreviado a REN (por renglón). De este modo me puedo referir a ellas más fácilmente.
También he mezclado la ciudad y estado en un solo campo, al cual he llamado CIUDADEDO (para
eso sirve el AS). De este modo puedo desplegarlo en un solo lugar para efectos de la factura. El
JOIN junta las tablas a través del campo indice (FacturaNo), que existe en las dos tablas, y en este
caso tiene el mismo nombre (ENC.FACNO = REN.FACNO). Finalmente, especificamos la
factura #1 únicamente para aventar eso a un reporte.
FACNO NOMBRECLIENTE CIUDADEDO ITEMNO DESCRIPCION PRECIO_USD
1 DAVID MARTINEZ MEXICO, DF 112232 COMPUTADORA COMPAQ PRESARIO $2,340
1 DAVID MARTINEZ MEXICO, DF 112522 MONITOR COMPAQ 21" $1,340
1 DAVID MARTINEZ MEXICO, DF 513213 3COM PALMIII $400.00
1 DAVID MARTINEZ MEXICO, DF 445653 LIBRO "MASTERING DELPHI 3" $39.99
1 DAVID MARTINEZ MEXICO, DF 445657 LIBRO "COLLAB COMP W/DELPHI 3" $69.99
La denormalización de los resultados es algo común en SQL. Recordemos que, para el servidor, no
es problema repetir los datos porque el cursor interno no se mueve de lugar. Y para nosotros, nos
ahorra tráfico en la red porque, aunque el set de resultados es más grande, no tenemos que pedir
más de uno. Los datos siguen normalizados en las tablas correspondientes.
Procedimientos
Vistas
Una vista (view) es uno o más comandos que devuelven un set de resultado y llevan consigo un
nombre. Para los programas que ejecutan comandos, la vista es identica a una tabla. De esta
manera el programador SQL puede juntar tablas y condiciones en una vista y cambiar los
parámetros de la misma. Por ejemplo, la siguiente vista repite el ejemplo de visualización de
facturas que vimos en la sección de SELECT y lo encapsula en una vista mucho más facil de usar.
CREATE
VIEW DESPLEGARFACTURA
AS
SELECT
ENC.FACNO,
ENC.NOMBRECLIENTE,
ENC.CIUDAD+ " , " +ENC.ESTADO AS CIUDADEDO
REN.ITEMNO,
REN.DESCRIPTION,
REN.PRECIO_USD,
FROM FACTURAS ENC
LEFT JOIN FACTURARENGLON REN ON ENC.FACNO = REN.FACNO
Note que en esta "vista" no tenemos el número de factura. Esto es intencional, para que podamos
escribir comandos como:
SELECT
* FROM DESPLEGARFACTURA WHERE FACNO = 1
Hay otros comandos de SQL que no devuelven un set de resultado. Estos comandos son
generalmente para preparar diferentes partes del servidor, o para modificar y borrar datos.
UPDATE
El comando UPDATE actualiza la tabla especificada con los valores que usted necesite. En el
ejemplo que estamos manejando, el siguiente comando modificará todos. El siguiente comando
cambia la abreviatura DF a su forma completa Distrito Federal:
UPDATE FACTURAS
SET ESTADO = "DISTRITO FEDERAL"
WHERE ESTADO = "DF"
DELETE
El comando DELETE borra los registros de la tabla especificada. Tal como Update, Delete acepta
una cláusula WHERE.
DELETE FACTURAS WHERE FACNO = 10
Este comando borrará la factura numero 10. Tal como en UPDATE, si no incluye una cláusula
WHERE en su comando, todos los registros de la tabla serán eliminados.
INSERT
El comando INSERT añade un registro a la tabla especificada. INSERT nunca acepta una clausula
where, pero INSERT puede añadir más de un registro basado en un SELECT.
Y este INSERT se combina con otro SELECT para copiar los renglones de la factura número dos y
ponerlos en la factura número 99.
Los sets de resultado de SQL pueden ser "vivos", es decir, Delphi (u otras aplicaciones) puede
modificar los registros sin necesidad de comandos UPDATE, INSERT o DELETE.
Lo primero que debemos hacer es crear una forma para que el usuario pueda hacer consultas. Así
que seleccione File-New en el menú de Delphi para crear una nueva forma.
Dentro de la página de "Dialogs", seleccione "Standard Dialog", ya sea el de los botones abajo o a
la derecha (según su preferencia).
En nuestra forma principal, añada un botón llamado "Consultar..." para que pueda mostrar el
diálogo. Seleccione "File-Use Unit" del menú para decirle a Delphi que la forma de consulta utiliza
el diálogo. Esto añade el diálogo a la clausula de USES en la sección implementation. Después
haga doble click en el botón para que Delphi escriba un evento, y escriba lo siguiente:
end;
En Delphi, cuando usted añade una palabra precedida por un signo de dos puntos (:), la palabra
es automáticamente convertida en un parámetro, el cual es agregado a la propiedad Params, la
cual es una lista de objetos que serán reemplazados por lo que usted especifique antes de
ejecutar el comando. Una vez que usted haya añadido el texto, Vaya a la propiedad "Params"
dentro del inspector de objetos y haga doble click. A continuación podrá ver una lista de
parametros que usted puede examinar (que por ahora solo contiene el apellido). Seleccione el
parámetro y modifique la propiedad "DataType" para que diga ftString. Es importante decirle a
Delphi que clase de parametro vamos a enviar para que Delphi sepa si le debe añadir comillas
(para especificar texto) o dejarlo como número.
Recordemos que el código SQL no es interpretado por Delphi, sino que es convertido a comandos
SQL para un servidor como Oracle o MS-SQL (vea el capítulo 6.1 para mayor información.
Ejecutemos el programa para probarlo. Ahora presione el botón consulta y escriba el nombre
"Davis" como apellido, y seleccione "Ok". A continuación verá a la Srita. Davis aparecer en la
pantalla. Experimente con distintos apellidos.
Si experimenta suficiente notará algo especial: Si usted sólo teclea una "D" en el campo de
apellido, también recuperará el registro! Hicimos esto aprovechando que el lenguage SQL soporta
la comparación LIKE, que devuelve todos los registros que se parezcan a la cadena que enviamos.
Cuando escribimos nuestro código en el OnClick, habrá notado que la línea que asigna el
parámetro añade un signo de porcentaje al final:
qryClientes.ParamByName('Apellido').AsString :=
dlgConsulta.edtApellido.Text+'%';
Esto quiere decir que queremos que el query nos devuelva todo lo que comienza con el texto
que tecleamos. Tenga en cuenta que el servidor SQL es el que interpreta este signo, así que como
se interpreta el signo de porcentaje depende del servidor que este proporcionándonos los datos.
Código de Ejemplo
Comenzando en esta sección estoy añadiendo archivos zip de ejemplo (ya que comenzamos a
escribir código). En el Zip de esta sección he avanzado de tal forma que el diálogo soporta
consultas a varios registros importantes.
Hay varias maneras en las cuales pude haber hecho esto, pero lo que decidí hacer para este
ejemplo fué crear varios componentes TQuery diferentes, uno con cada tipo de consulta. Despues
añadí un RadioButton al diálogo de consulta para que el usuario decida qué tipo de consulta voy a
hacer. El radioButton cambia la etiqueta del campo para reflejar la consulta adecuada.
Notará que, aunque hay varios Queries, solo hay un DataSource. Este es otro ejemplo de la
versatilidad de Delphi. Para hacer que todos los campos de la forma apunten a otro query, todo lo
que tengo que hacer es modificar la propiedad DataSet del componente DataSource para apuntar
a esta nueva consulta, y todos los componentes visuales que apuntan al DataSource
automáticamente cambiarán a la consulta adecuada. Estoy tomando esta decisión aprovechando
la selección en el RadioButton. El código que realiza la consulta ahora tiene la siguiente
apariencia:
end; // if
end;
Es aquí donde los míodulos de datos nos pueden ayudar. Estrictamente hablando, un módulo de
datos es una "Forma Invisible" que nos permite poner nuestra lógica de acceso de datos separada
de la interfaz del usuario. Usted puede tener uno o varios módulos de datos para su aplicación.
Por ejemplo, supongamos que Ud. tiene que programar un sistema maestro para su pequeña
empresa. Usted ha determinado que necesitará escribir un sistema con las siguientes áreas
functionales:
Facturación
Ventas
Control de Inventarios
Aplicación al Sistema Contable en Oracle
En este ejemplo, usted puede decidir hacer módulos de datos para cada área funcional. Recuerde
que esto no tiene nada que ver con su interfaz de usuario; sino con la manera en que los datos
están representados, y las reglas del negocio para cada departamento. Incluso, si su organización
es muy grande y los chavos del sistema contable tienen su sistema en Delphi, usted puede
heredar desde el Repositorio de Código el código del sistema contable que fue hecho en Delphi, y
únicamente hacer los cambios apropiados para que su sistema funcione.
Los módulos de datos se pueden usar unos a otros (utilizando File-Use desde el menú de Delphi)
para encadenar las funciones. Pero usted deberá tener cuidado de mantener una separación
funcional correcta. Para que una forma vea un módulo de datos, éste debe haber sido creado
previamente a la forma, y el archivo que contiene el módulo de datos debe existir en la cláusula
uses de la forma.
Como cualquier otra forma, los módulos de datos tienen su propio tipo cuando son creados. Un
módulo de facturación que usted ha llamado dmFacturas sería de tipo TdmFacturas. Esto le
permite crear más de un módulo de datos a la vez, todos compartiendo el mismo código (aunque
tal vez con una factura diferente abierta.
Otra ventaja de los módulos de datos es cuando usted comienza a distribuir su aplicación
utilizando tecnologías como MIDAS. En este caso, el servidor crea instancias del módulo de datos
conforme las va necesitando, para que los clientes remotos tengan un módulo de datos donde la
aplicación haga los cálculos de negocios.
Desde el menú de Delphi, seleccione File-New. En el diálogo de "New Items" encontrará el Data
Module en la primera página. Selecciónelo y presione OK. Delphi nombra nuestro nuevo módulo de
datos DataModule1.
Grábelo en disco con el nombre "datamoduleclientes" para que tengamos un nombre al cual
referirnos cuando lo comencemos a usar desde otros lugares.
Ahora necesitamos mover los queries que tenemos en la forma principal a nuestro nuevo
datamodule. Esto es tan sencillo como seleccionarlos, y cortar y pegar.
Ahora cambie la propiedad "Name" del datamodule para que sea significativa. En mi ejemplo lo
nombré "dmClientes".
Si usted es perceptivo, habrá notado que los elementos TField que pertenecen a
estos queries también han sido copiados, junto con todas las propiedades
(nombres de campo, formatos, etcétera) que usted ha definido con anterioridad.
Este fragmento del código de nuestro módulo de datos nos muestra que los
TFields han sido copiados:
unit datamoduleclientes;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, DBTables;
type
TdmClientes = class(TDataModule)
qryClientesporApellido: TQuery;
qryClientesporApellidoLAST_NAME: TStringField;
qryClientesporApellidoFIRST_NAME: TStringField;
qryClientesporApellidoACCT_NBR: TFloatField;
qryClientesporApellidoADDRESS_1: TStringField;
qryClientesporApellidoCITY: TStringField;
qryClientesporApellidoSTATE: TStringField;
qryClientesporApellidoZIP: TStringField;
Ahora necesitamos decirle a la forma principal que los datos provienen no de los queries en la
forma principal (por ejemplo qryClientesporApellido), sino de los queries en el módulo de datos
(por ejemplo dmClientes.qryClientesporApellido). Seleccione la forma de Clientes y vaya al menú
"File-Use Unit". A continuación podrá ver una lista de las formas que su forma principal no está
usando. Seleccione "datamoduleclientes".
Seleccione el DataSource de la forma de Clientes. Notará que cuando cortó los TQueries para
pegarlos en otro lugar, la propiedad DataSet se borró. Si usted abre la lista, Delphi encontrará los
TQueries, ahora nombrándolos "dmClientes.(nombre del query)".
Esto nos dá una última pista: En el código del botón de consultas, en cualquier
lugar donde usted mencione uno de los queries, debe ahora añadir el calificador
"dmClientes." antes del nombre, para que el compilador lo encuentre. Una
manera fácil de hacer esto es con la sentencia "with":
Este no es un problema de cómputo, sino humano. Las agilidad de un negocio impide el mantener
un modelo o conocimiento de las "reglas del negocio" en un lugar legible y centralizado. Si usted
centraliza y documenta todos los procedimientos perfectamente, su negocio tiende a volverse
rígido. Cuántas veces hemos oido "lo haría por usted, pero la computadora no me deja". Pero al
mismo tiempo, el hacer sistemas flexibles que al mismo tiempo satisfagan las necesidades del
usuario es mucho muy dificil sin un conocimiento claro de las reglas.
Muchas veces tenemos que hacer tremendos malabares para implementar exactamente lo que el
usuario requiere. Aún con la mejor documentación y requerimientos, siempre acabamos en un ir y
venir con los usuarios relevantes para implementar ciertos cálculos o procedimientos que no son
claros para nadie más que para uno o dos usuarios en específico.
Con el correr de los años, los sistemas de cómputo se volvieron no sólamente la herramienta de
todos los días, sino el único repositorio actualizado de todo el conocimiento acerca de cómo un
negocio funciona. El problema es que este conocimiento está expresado en COBOL, Pascal, Java,
C++, SQL y muchos otros lenguajes que la gente común y corriente no entiende, y algunas reglas
muy importantes se encuentran dentro de ciclos "for-next" y "do-while".
Al darse cuenta de esto, y tomando en cuenta el hecho de que la orientación a objetos puede
reflejar más fielmente la realidad, varios científicos de cómputo y eruditos decidieron que si
pudieramos diseñar sistemas de cómputo utilizando modelos del mundo real (objetos que
representen sus contrapartes en el mundo físico), y aplicaramos a esos modelos procedimientos y
acciones relativas a otros objetos, podríamos lograr:
Documentos de diseño que reflejan el mundo real y las reglas del negocio, legibles tanto
por los administradores como por los ingenieros
Sistemas más flexibles, donde añadir la capacidad de hacer algo quiere decir simplemente
añadir un método
Separación entre la interacción de los objetos "de negocios" y la interfaz del usuario,
permitiendo usar la misma lógica para introducir los datos ya sea desde el teclado, mouse,
navegador de web, realidad virtual o desde otro programa via internet
Es muy importante tomar en cuenta de que la primera vez que realizamos un sistema de esta
forma el diseño del sistema tomará mucho tiempo y muchas iteraciones y diagramación antes de
escribir una sola línea de código o diseñar una sola pantalla o tabla.
Para tomar prestada una analogía de la notación UML, diseñar sistemas de objetos de negocios es
un poco como escribir una obra de teatro. El escritor comienza por definir Actores (objetos),
especificando su naturaleza, historia (razon de ser) y sus interacciones con otros Actores. Al hablar
con los expertos que utilizan estos "actores" una y otra vez descubrimos más interacciones que
reflejan más actores que a su vez interactúan con otros actores, nuevos o existentes.
Al final lo que tenemos es un modelo muy exacto de como funciona el negocio, pero en papel. Una
vez hecho esto, podemos comenzar a definir bases de datos para guardar las características de
los diferentes actores, código que representa los actores y todas sus interacciones, y pantallas
(vestuario y coreografía) para llenar las características de los actores, con botones y eventos para
activar sus acciones.
Programación Multi-Capas
Cuando crece su empresa, o la cantidad de computadoras que usted maneja, el mantener todos
los clientes configurados correctamente y con la más nueva versión de su programa instalado
puede resultar complicado.
Supongamos que su programa mantiene e imprime facturas para sus clientes. El aparato fiscal de
su país acaba de bajar los impuestos de ventas (jajajaja), y además ahora se calculan de manera
diferente dependiendo del tipo de producto. Si usted tiene que reinstalar el programa en 50
tiendas simplemente porque el impuesto ha bajado, tendrá el riesgo de que algunas de las tiendas
calcularán los impuestos de manera incorrecta hasta que usted instale el programa en todas las
tiendas.
Tal vez usted dirá: "Bueno, incluso si esto es cierto, puedo poner una tabla centralizada con el
porcentaje de impuestos". Si bien esto es cierto, ¿qué hará cuando lo que cambia es la manera de
calcular? Esto ocurre muy seguido en el cálculo de impuestos en general en todos los países. Es
muy dificil escribir un programa para interpretar fórmulas en una base de datos, especialmente
cuando estas fórmulas pueden ser muy complicadas, con procedimientos, diferentes casos y
repeticiones.
El paradigma de objeto de negocios es muy útil, pero nos ocasiona muchos problemas que Delphi
ya había solucionado en cuanto a la programación de base de datos. El diseño de los objetos de
negocios en sí totalmente ignora el concepto del DataSet, y con ello, los controles data-aware.
Esto quiere decir que, en una implementación "pura" de un objeto de negocios, usted deberá de 1)
utilizar controles normales y escribir una gran cantidad de código de infraestructura, o 2) crear su
propio descendiente de TDataSet que funcione con cada uno de sus objetos de negocios.
Esto es un problema, especialmente cuando nos ponemos a pensar que tanto COM como CORBA
son traducidos a llamadas locales en tiempo de compilación; lo cual quiere decir que es imposible
convertir una interfase de objetos de negocio a un TDataSet sin tomar en cuenta la interfase de
objeto de negocio en específico.
He puesto la sección de MIDAS antes que las secciones de DCOM y CORBA intencionalmente
porque usted no necesariamente debe saber de interfases para comenzar a utilizar MIDAS.
Esto no quiere decir que el aprender interfases no le sea útil, sino que usted puede hacer
programas pequeños y medianos con MIDAS y exportarlos como objetos CORBA o DCOM sin
aprender nada de estas tecnologías, gracias a los Wizards de Delphi.
En este caso, nuestra aplicación anterior se convertirá en un "cliente" de la aplicación que realiza
el acceso a bases de datos.
Primero necesitamos crear un proyecto nuevo para nuestra "capa intermedia". Seleccione "File-
New" y después "Application". Esto creará un proyecto básico con una forma.
A continuación necesitamos crear nuestra interfase para que nuestros clientes la encuentren.
Utilice "File-New" y Seleccione "Remote Data Module" desde la página "Multitier".
A continuación, el wizard le pedirá los datos necesarios para generar el nuevo módulo de datos.
Class Name: Este es el nombre de la clase con la cual usted publicará el módulo de datos.
Cuando un programa cliente pida una interfase por nombre, éste es el nombre que deberá
pedir para que el servidor responda.
Instancing: Este es el modelo de instanciación que el módulo de datos utilizará.
Threading Model: Este es el modelo de hilos de ejecución que utilizará su programa.
Ahora grabe su nuevo módulo de datos (el nombre del mío fué "dmTutorClientes.pas".
El módulo de datos que usted acaba de crear es además una interfase. Esta interfase es pública
una vez que se haya registrado con la computadora, y otros procesos la pueden accesar. Así que
ahora todo lo que debe usted hacer es crear queries y exportarlos. Para ahorrar tiempo, copie y
pegue los queries del proyecto de Clientes a este. Una vez que haya copiado y pegado, terminará
con un módulo de datos público con tres queries que aún son privados. Para exportarlos, haga
click con el botón derecho del mouse y seleccione "Export Query from DataModule" del menú
de opciones. Haga lo mismo con los tres queries. Una vez hecho esto, compile y ejecute el
programa una vez para que el sistema registre la existencia de estas nuevas interfases.
MIDAS e Interfases
Delphi exporta interfases utilizando una librería de tipos. Una librería de tipos es un archivo que
mantiene la especificación de las interfases. Como MIDAS necesita exportar su módulo de datos
como una interfase, utiliza el editor de tipos para generar interfases de manera estándar.
Nuestro proyecto ha exportado una interfase con tres propiedades. Para verlas, seleccione "View-
Type Library". A continuación podrá ver la interfase TutorClientes ("ITutorClientes"), contenida en
una CoClass llamada "TutorClientes". Esta interfase tiene tres propiedades, con los nombres de los
queries que usted ha exportado.
Estas propiedades son de tipo IProvider. IProvider es una interfase específica de MIDAS, que se
utiliza para comunicar un DataSet con sus clientes.
Ahora modificaremos el cliente para que se comunique con el servidor. Abra el proyecto de
Clientes.dpr y elimine todos los queries del Módulo de datos. También deberá eliminar las
menciones a dbtables y bde (si la tiene) de su módulo de "uses", tanto en el módulo de datos
como en las formas del programa. Esto no sería necesario si usted nunca hubiera tenido
TQueries en su programa.
Una vez que ha hecho eso, necesitará seleccionar el IProvider correcto para cada ClientDataSet.
Asigne la propiedad ProviderName de cada uno de los TClientDatasets dependiendo del nombre.
Como eliminamos los queries, usted necesitará cambiar el DataSource de la forma principal para
especificar el nuevo ClientDataSet, y cambiar un poco de código en el botón "Consultar" para que
utilice el ClientDataSet en vez del TQuery (esto requerirá que usted añada "dbClient" a la clausula
de uses del programa principal, para poder declarar la variable local como TClientDataSet
(recuerde eliminar dbTables porque ya no estamos usando TQueries).
Note que al final he añadido el comando "FetchParams" para que el cliente pida la lista de
parámetros del servidor (esto no ocurre automáticamente).
Por ahora, mantenga la propiedad Connected del TDComConnection encendida (True) para que no
tenga que conectarse. Usted podrá hacer esto más tarde con un botón de "Conectar" o algo así.
Ahora puede usted ejecutar el programa. Notará que funciona de la misma manera que los
programas anteriores, sólo que éste no requiere el BDE y funciona de manera distribuida.
Cualquier regla que usted aplique a los TQueries y a los TFields dentro de los queries será copiada
al cliente.
Le recomiendo mucho que estudie TClientDataset y lo aplique. Es muy útil aún sin necesidad de
un servidor de aplicaciones.
Un componente es un objeto que "sabe" como comportarse en modo de diseño, y puede ser
añadido a una forma. Todos los objetos de la barra de componentes de Delphi heredan directa o
indirectamente de TComponent, que es de donde heredan su comportamiento en tiempo de
diseño, y sus capacidades de añadirse a y grabar su estado en el archivo de Forma (dfm).
Como Delphi es completamente orientado a objetos, usted puede heredar un objeto que se
comporta exactamente como el objeto del cual heredó. Este concepto es lo que hace tan sencillo
el extender la barra de herramientas de Delphi para nuestros requerimientos.
Paquetes
La tecnología de paquetes es parte integral de la habilidad que tiene Delphi para extender sus
capacidades usando componentes y ayudas de desarrollo. Usted también puede utilizar la misma
tecnología para extender sus propios programas.
Todos los componentes viven en un paquete. Un paquete es una clase de DLL especial que tiene
funciones para pasar tipos de datos especiales (los DLLs, en su forma nativa, sólo pueden pasar
cadenas de texto, números y punteros). Los paquetes de Delphi son integrales para el diseño de
componentes, porque cuando un componente está en modo de diseño Delphi utiliza la librería de
paquetes para cargarlo en memoria.
Usted puede Activar y Desactivar paquetes con el menú de "Project-Install Packages" en Delphi.
Además, cuando usted abre un proyecto, el proyecto recuerda los paquetes que usted estaba
utilizando y los activa, para asegurarse de que todos los componentes estén funcionando cuando
usted despliegue la forma.
Usted puede crear sus propios paquetes en Delphi. Un archivo fuente de cabecera de un paquete
tiene la extension .dpk. Un paquete compilado tiene la extension .dpl.
Tipos de Componentes
Por sí solo, un TComponent no puede hacer nada más que exportar las propiedades de su sección
"published" en formato DFM, y mostrar una cajita en la forma que no se muestra en modo de
ejecución (esto es, un componente no visual). Así que rara vez querrá usted crear un
componente "desde cero". Lo que usted probablemente hará es extender un componente
existente, o al menos utilizar algunos de los descendientes abstractos directos de TComponent
para crear su propio componente.
Es por esto que todas las versiones de Delphi en vez de la estándar vienen con el código fuente de
la librería de componentes. A veces, cuando heredamos componentes, es importante asomarnos a
ver cómo los programadores de Borland escribieron métodos específicos, para saber si podemos
reutilizar este código o si necesitamos hacer override al código existente (o en la mayoría de los
casos, alguna combinación de ambos).
Reglas para Crear un Componente
Un componente es muy, muy parecido a una clase, pero escribir un componente debe tomar en
cuenta las siguientes reglas:
Regla #1. Las propiedades para el Inspector de Objetos van en la sección published
Cuando usted crea un componente y desea que su propiedad sea parte del inspector de objetos,
usted deberá declararlas en la sección published de su componente. Las propiedades de la
sección published son mostradas en el inspector de objetos y son grabadas en el archivo DFM.
Para crear un componente, es obligatorio hacer métodos GetXXX y SetXXX para cambiar un
miembro.
private
procedure SetVariable( Value : String );
function GetVariable: String;
protected
FVariable : String;
published
property Variable : String read GetVariable write SetVariable;
Esto se hace así para permitirle a usted hacer tareas automáticas, como inabilitar el control
cuando la variable tiene un valor específico, o cambiar el color de un control automáticamente
cuando el valor de una propiedad cambia. En este ejemplo, el color del control cambia a verde
cuando la propiedad Variable tiene la cadena "Hola Chavos".
procedure Register;
begin
RegisterComponents('Samples', [TSuComponente]);
end;
El crear un componente es una disciplina con preceptos muy sencillos, pero es un arte. Usted
puede hacer desde cambios pequeños como el hacer un TLabel que imprima "al revés", hasta la
creación del widget para realidad virtual más picudo del planeta.
Nuestro primer componente es un Label que dice "Buenos Días" en varios idiomas.
Por ejemplo, usted puede hacer su propio ComboBox, pero si el componente que usted quiere
hacer difiere mucho de TComboBox, podría ser más recomendable heredar de
TCustomComboBox, que provee los primitivos de un ComboBox pero mantiene muchas
propiedades privadas.
Ahora presione el botón "Install..." para decirle a Delphi que queremos añadirlo a un paquete.
Seleccione "existing package" (pudo haber creado un paquete nuevo desde aquí tambien
utilizando "Into New Package"), y seleccione nuestro Tutor.dpk.
Note que ahora hay una página "Tutor Delphi" en su paleta de componentes, con un componente
THolaLabel en ella.
Ahora podemos comenzar a escribir código. Nuestro "proyecto" es ahora el archivo DPK, así que
para compilar simplemente utilizamos el Botón Compile de la ventana del Paquete.
type
TIdioma = ( idEspanol, idIngles, idFrances,
idItaliano, idAleman );
THolaLabel = class(TLabel)
Ahora que tenemos un tipo que nos será útil para definir los idiomas, necesitamos
una variable y funciones set y get para guardar qué idioma vamos a usar.
private
{ Private declarations }
FIdioma : TIdioma;
procedure SetIdioma( Valor : TIdioma );
function GetIdioma : TIdioma;
Por supuesto, no basta con definir las funciones; también debemos escribirlas:
FIdioma := Valor;
case FIdioma of // Dependiendo del idioma es el mensaje.
idEspanol : sMensaje := 'Buenos Días!';
idIngles : sMensaje := 'Good Morning!';
idFrances : sMensaje := 'Bonjour!';
idItaliano : sMensaje := 'Bonjorno';
idAleman : sMensaje := 'Gutten Mörgen!';
end;
Self.Text := sMensaje; // Reemplaza nuestro texto con el mensaje
end;
Note que la funcion SetIdioma no sólo guarda el idioma, sino cambia el texto del Label (self) de
acuerdo al idioma que el usuario seleccionó. Esto nos permite que el Label muestre el cambio de
inmediato al cambiar mi propiedad, incluso en modo de diseño!
published
{ Published declarations }
property Idioma : TIdioma read GetIdioma write SetIdioma;
Felicidades! Acaba usted de hacer su propio componente. No fué difícil, verdad? Por cierto, el
concepto de JavaBeans en Java es casi idéntico a la creacion de componentes en Delphi (y no por
casualidad; Borland está en la mesa directiva del consorcio de estándares de Java).
Ahora necesitamos probarlo, por supuesto. Como ya está instalado, sólo tenemos que recompilar
el paquete. Vaya a la ventana de "Tutor.dpk" y presione el botón "Compile".
Note que al compilar para modo de Diseño, Delphi analizó su código y su propiedad Idioma es
perfectamente legible, utilizando los mismos valores que usted escribió en su nuevo tipo TIdioma.
Aún con esta ayuda en modo de diseño, el optimizador compilará un valor entero para mayor
velocidad. Pero por ahora, usted puede cambiar la propiedad. Cambiemos nuestro HolaLabel a
italiano:
He cambiado mi Font a algo más grande para que sea legible. Cuando usted cambia la propiedad,
Delphi ejecuta la función SetIdioma que usted ha escrito, y cambia el texto a italiano.
Aunque esto es muy útil, recuerde que Delphi esta ejecutando su código, lo cual quiere decir que
cualquier loop infinito o error en el código que Delphi ejecuta en modo de diseño puede trabar a
Delphi. Asegúrese de grabar antes de compilar sus paquetes.
Hacer un Control para su uso en Visual Basic y otros lenguajes es tan sencillo que ni siquiera
amerita un capítulo propio. En esta pequeña sección (que no es necesaria si usted no quiere
utilizar su componente en otros lenguajes) crearemos un control OCX.
Automáticamente, Delphi creará los archivos necesarios. Ahora sólo compile e instale el .OCX
resultante en VB. Felicidades! Acaba de hacer usted un control OCX!
Copiar el Código fuente (7K, formato Zip) para esta sección (Componente/Paquete)
Copiar el Código fuente (29K, formato Zip) para esta sección (ActiveX)
Si usted ha viajado a otro continente, seguramente estará consciente de que algunos países
tienen diferentes enchufes eléctricos. Argentina, por ejemplo, tiene enchufes redondos; Los
estados unidos tienen enchufes de tres picos, donde dos de los picos son planos y el otro (el de
"tierra") es redondo. Pero también funcionan los aparatos viejos porque solo tienen los dos picos
planos y los dos picos planos dan la misma corriente eléctrica que requieren estos
aparatos. Los enchufes eléctricos no son exactamente alta tecnología, pero es el ejemplo más
sencillo de una interfase.
Definición de Interfase
Otro ejemplo de una interfase es el siguiente. Seguramente usted habrá oido en las noticias que el
taxi espacial Estadounidense ha estado viajando y conectándose con la estación espacial Rusa
MIR. La tecnología rusa y la estadounidense son muy diferentes. ¿Cómo es esto posible? Bueno,
cuando construyeron MIR hicieron una definición de como se debía fabricar la interfase para
acoplamiento con el MIR. Por ejemplo, seguramente definieron la forma (redonda), dónde iban los
enchufes de los tubos para pasar oxígeno, electricidad, combustible, etc. Si usted quisiera acoplar
su casa con MIR, tendría que construir una interfase redonda con los enchufes de la misma
manera, y entonces su casa podría conectarse a MIR.
Basado en estos ejemplos, mi definición sencilla de interfase para usted es: Una interfase es una
especificación fija para que dos objetos hablen entre sí.
Esto ha funcionado muy bien hasta ahora, pero tiene dos problemas: El primero es que los otros
sistemas siempre tienen información que está atrasada por un mes. Esto es, si el sistema de
ventas no sabe que el cliente se mudó a otra dirección la semana pasada, el producto le llegará a
la dirección anterior aún si el cliente cambió su dirección con los muchachos de servicio al público,
porque la información del cliente no ha sido copiada al sistema de ventas.
El otro problema es que los programadores tienen que "reinventar el hilo negro" quince veces
para hacer consultas de clientes funcionar en todos los sistemas, y como los datos de todos los
clientes ocupan 2Gb, usted está gastando 2Gbx15 departamentos diferentes en datos
redundantes.
Lo que usted necesita es una manera de hacer que los diferentes subsistemas (ventas, cobros,
envíos, garantías) le hablen al mismo sistema de clientes cada vez que necesiten información
acerca del cliente. De esta manera usted ahorra 28Gb de espacio en disco y elimina trabajos
nocturnos, permitiendo el uso del sistema en la noche para la gente que trabaja horas extras.
Como verá esto tendría muchas ventajas.
Esta es la clase de problema para el cual las interfases fueron diseñadas. Para solucionar éste
problema lo primero que usted debe hacer es especificar una Interfase para que los diferentes
subsistemas puedan platicar con su sistema de clientes. Comencemos a definir una interfase de
manera teórica.
Nota: Es muy importante pedir ayuda de la gente que trabaja en todos los
departamentos para que la interfase mantenga a todos satisfechos.
Recuerde que no importa cuanto sepa usted, son los usuarios los que
saben la clase de datos que necesitan.
Supongamos que usted organizó una junta. Para invitar a la gente en esta junta usted necesitó los
jefes o supervisores de todos los departamentos que requieren datos del cliente, porque ellos son
los que saben todo acerca de los datos que deben ser transportados por las interfases) y también
a los líderes de proyecto de todos los sistemas que utilizan datos del cliente (porque ellos
necesitan saber y opinar acerca del diseño de la interfase). Entre todos han llegado a un diseño
general que contiene lo siguiente:
El diseño puede ser a discreción de las necesidades de cada compañía, pero uno de los diseños de
la interfase de clientes (pseudocódigo) es como sigue:
El Lenguaje IDL
El lenguaje IDL es un lenguaje común para definir interfases tanto en COM como en CORBA. IDL se
parece a C, pero carece de sentencias de control (do while, for next, etc). Esto es porque en IDL
usted unicamente define las funciones que usted va a implementar en Pascal, C, Java, COBOL,
etcétera.
IDL es un estándar, y cada lenguaje (Delphi, Java, C, etc.) cuenta con un traductor de IDL, que
traduce las sentencias de la definición de la interfase al lenguaje adecuado.
Delphi es facil de usar y le permite diseñar las interfases visualmente. Usted puede obtener el
código IDL utilizando el botón "Export to IDL" de la barra de botones del editor de tipos y
seleccionando "Export to CORBA IDL". Como de costumbre, el IDL estándar es diferente al IDL de
Microsoft. La función "Export to IDL" puede exportar a ambos tipos. He aquí el resultado de
seleccionar CORBA IDL y MIDL (Microsoft IDL), basandonos en el ejemplo anterior:
CORBA IDL
Microsoft IDL
De esta manera, cuando usted necesite exponer interfases a usuarios de otros lenguajes (Java, C+
+, Visual Basic), usted puede utilizar estas funciones para generar un archivo IDL que ellos puedan
usar.
Punteros de Interfase
Ahora supongamos que todos los sistemas deben comenzar recuperando un Cliente. Los sistemas
piden una interface a IClientes en la computadora y piden a la computadora XYZ (utilizando
tecnología COM, CORBA, etc) que genera una "instancia" (instance) de la interface IClientes. El
sistema nos va a devolver lo que se llama un puntero de interfase (interface pointer). Este
puntero de interfase es como la conexión al sistema de clientes. Representa la interfase de
clientes que la computadora XYZ soporta (obviamente, si pedimos una interfase IClientes a una
computadora donde el sistema de clientes no existe, recibiremos un error tipo "Interface not
supported"). Nunca trate de guardar este puntero en un archivo de texto o algo así porque solo es
un número de una posición de memoria.
Una vez que usted obtenga esa interfase, usted llamaría al cliente #1 utilizando la función
DameCliente(1). El resultado de esta función debe ser guardado en otro puntero de interfase.
Ahora nuestra segunda interfase es ICliente.
El miembro de la interfase es aún otra interfase, ésta vez llamada IPersona. Y así sucesivamente
hasta que usted encuentra finalmente un tipo de datos primitivo que pueda desplegar en la
pantalla (String, LongInt, etc). Obviamente, como estas son interfases y son soportadas en varios
lenguajes, no mencioné un lenguaje en específico, sino solamente los conceptos.
Así que ya hemos visto cómo los clientes van a utilizar nuestra interfase. Pero nosotros, cómo la
implementamos?
Object pascal, como hemos visto anteriormente, es orientado a objetos. Cuando queremos que un
objeto (cualquier objeto) represente una interfase, el primer paso es decirle al compilador que
esto es lo que necesitamos hacer, agregando una coma y el nombre de la interfase a la definición
de tipo de la unidad. Por ejemplo:
En otros lenguajes usted debe programar los métodos de IUnknown por sí mismo, pero Delphi
hace todo esto por usted mientras herede de el objeto TInterfacedObject o de TComObject.
Una vez que hemos declarado los objetos que van a representar nuestras interfases (usted deberá
declarar también objetos para representar IPersona e ICliente), su programa no compilará hasta
que declare funciones para satisfacer todos los procedimientos y funciones de la interface. Estas
funciones normalmente se declaran en la sección protected del objeto. Por ejemplo:
Como algunos lenguajes no son orientados a objetos, el lenguaje IDL nos obliga a utilizar métodos
llamados Get_XXX y Set_XXX cada vez que creamos una propiedad de lectura y escritura (si la
propiedad es de Lectura solamente, solo debemos satisfacer la función Get_XXX). Es mucho
trabajo, pero como veremos en la práctica, si usted utiliza Delphi la mayor parte de este código es
escrito por usted mientras diseña. Pero es importante que Ud. lo entienda por si algo sale mal o
expertos en otros lenguajes necesitan entender algo acerca de su interface.
Así funciona la teoría de las interfases, y así es como se programan en Delphi. Dependiendo del
tipo de interfase que usted vaya a programar, usted deberá utilizar un servicio o registro para
decirle a la computadora que la implementación de su interfase de IClientes existe en la
computadora XYZ y el programa se llama ServicioClientes.EXE (o algo así). Veremos estos
conceptos y un programa de ejemplo en los subcapítulos que siguen.
Ahora que sabemos cómo funciona una interfase y como se implementa en Object Pascal, veamos
cómo funcionan las interfases COM (y DCOM) en específico.
COM es un transporte para interfases. La conveniencia de COM es que viene con todas las copias
de Windows en el planeta, así que no hay que preocuparse de instalar librerías con su sistema. La
parte inconveniente es que si usted utiliza COM, su programa solo puede hablar con máquinas que
corran Windows (hay un esfuerzo para implementar la librería COM en Unix, pero no ha tenido
éxito técnico o político).
Ejecutables en Windows
La implementación de una interfase COM en Delphi puede vivir en cualquier archivo de 32 bits que
Windows pueda ejecutar. Estos archivos pueden ser ejecutables, Librerías DLL o Librerías OCX.
Hay ciertos detalles en cuanto a lo que es soportado en cada uno de estos tipos de
implementación, como hilos de ejecución o capacidad de funcionar como un control de ActiveX. Un
solo ejecutable puede implementar todas sus interfases, o puede usted granularizar en diferentes
servicios como más convenga a su caso en particular.
Para poder instanciar una interfase, la computadora necesita saber en qué ejecutable vive esta
interfase, y además necesita saber algunos otros detalles acerca de su ejecución, como puntero
de entrada a cada una de las interfases (dentro del ejecutable), permiso de usuario con los cuales
el ejecutable va a funcionar, etcétera. Toda esta información es guardada por el registro de
Windows.
Lo primero que necesitamos para una interfase es no confundirla con otra. Para esto se utilizan los
llamados GUIDs, o Globally Unique IDentifiers. Estos identificadores son una especie de número de
serie aleatorio de 128 bits generado utilizando una función llamada CoCreateGUID. La función está
escrita de manera tal que se le garantiza que éste numero nunca será repetido. Esto es
importante porque el GUID es el identificador que la computadora utilizará para nuestra interfase
en todas las computadoras de todo el mundo, y no queremos que nuestras interfases se
confundan con las de otra persona. Por ejemplo, en todo el planeta, el siguiente GUID identifica al
Active Desktop de Internet Explorer:
{FBF23B40-E3F0-101B-8488-00AA003E56F8}
Tómese unos cuantos minutos para examinar el registro de Windows con el programa REGEDIT.
Usted podrá observar que uno de los folders de "raíz" se llama HKEY_CLASSES_ROOT. Este folder
contiene toda la información de todas las interfases. Su nombre, su identificador único, sus
permisos, etcétera. Windows utiliza COM durante su operación diaria, así que usted encontrará los
GUIDs de interfases de Windows, Borland, y todas sus aplicaciones junto con sus propias
interfases.
Utilizar regedit para ver sus interfases puede resultar complicado. Microsoft tiene funciones para
averiguar información acerca de las interfases sin tener que accesar el registro, y se reservan el
derecho de cambiar el formato del registro. Así que dentro de lo posible, procure que su programa
utilice las funciones del Windows API para averiguar información acerca de las interfases en vez de
usar el registro.
Instanciando Interfases
Los programas en Windows pueden ser muy diferentes. ¿Cómo sabe Windows cómo instanciar una
interfase? Cuando el programa inicializa (antes de comenzar a ejecutar), Delphi crea por nosotros
una "Fabrica de Objetos" (Class Factory). Una fábrica de objetos es un objeto concreto que genera
objetos de la clase cada vez que Windows decide que un programa cliente ha solicitado un
puntero de interfase. Si usted utiliza las capacidades de RAD de Delphi, rara vez se tendrá que
preocupar acerca de esto, pero es importante que tenga en mente como funciona para resolver
algunos problemas que no son obvios. Por ejemplo, si por algún motivo su objeto nunca inicializa y
usted nota que el procedimiento create nunca ejecuta (y el objeto está registrado), el problema
puede ser que la fábrica de objetos nunca está siendo creada. Si no hay fábrica de Objetos,
Windows no puede crear una CoClass.
implementation
uses ComServ;
initialization
TAutoObjectFactory.Create(ComServer, TClientes, Class_Clientes,
ciMultiInstance, tmApartment);
end.
TAutoObjectFactory es una fábrica de clases que genera objetos COM. El Create crea una nueva
fábrica de clases y la añade al ComServer (un objeto global dentro de la unidad ComServ que
mantiene una lista de todas las fábricas de clases en este ejecutable). También registra el hecho
de que va a instanciar objetos de Delphi TClientes cada vez que Windows necesite una CoClass
llamada Class_Clientes (esta CoClass fué generada por la biblioteca de tipos del proyecto
automáticamente, y tiene asociado un GUID para ponerlo en el registro de Windows. Vea el
capítulo práctico para entender como funciona con un ejemplo).
También notará que el Create le notifica a la fábrica de clases que este objeto es de instancia
múltiple y su modelo de hilos de ejecución es "Apartamento".
Por ejemplo, las siguientes gráficas ilustran cómo hace COM para instanciar un objeto a partir de
una fábrica de Clases. Cuando un cliente pide la interfase IClientes (1), COM busca en el registro
de Windows el GUID que corresponde a IClientes (a menos que usted haya especificado el GUID en
vez del nombre), y después busca el ejecutable que corresponde al GUID. Si el archivo no está en
memoria, lo ejecuta (2). Una vez hecho esto, COM busca en la lista de la librería de clases (que
Delphi guarda en el objeto global ComServer) la fábrica de clases (en rojo) que puede crear
IClientes (representado por Class_Clientes en el código fuente arriba) (3).
Después, COM llama al método de la fábrica de clases para crear un objeto, y la fábrica de clases
crea un objeto TClientes (4) (usando el método TComObject.CreateFromFactory) y asigna un
puntero de interfase, el cual es devuelto a COM (5).
COM a su vez crea un puntero de interfase interno para devolver al cliente (6).
¿Porqué el paso 6? Recuerde que en un ambiente de memoria protegida como es Win32, usted no
puede accesar memoria que no le pertenece a su aplicación, así que COM necesita crear un
puntero para usted. Esto quiere decir que usted no puede comparar los numeros de puntero en el
lado del servidor y del cliente y esperar que sean el mismo. Esto también es util cuando
instanciamos un objeto usando DCOM en otra computadora (donde la posición de memoria no
tiene sentido en nuestra máquina).
Usted notará que lo complicado en COM ocurre en el servidor, no en el cliente. Lo único que el
cliente tiene que hacer es llamar CreateComObject para obtener un puntero de interfase y usarlo.
Toda la traducción y lo "Feo" se encuentra en el lado del servidor.
Single Instance
En este modelo, la primera vez que un cliente pide un puntero de interfase, la fábrica de
objetos crea nuestro objeto. Pero la segunda vez, la fábrica de objetos simplemente
encuentra la dirección de nuestro primer objeto y da esta dirección al cliente en vez de
crear un nuevo objeto. De esta manera usted tiene la garantía de que sólo un TCliente
estará en memoria dando servicio a todos los programas clientes.
Multiple Instance
Bajo este modelo, cada vez que un cliente pide un nuevo puntero de interfase, la fábrica de
objetos crea un nuevo objeto. Bajo este modelo usted debe tener cuidado con los nombres
de sus objetos para no confundirse, porque usted literalmente tendrá varios objetos
TCliente rondando por ahí (uno por cada cliente).
Bajo este modelo, hay solo un hilo de ejecución. Todos los programas esperan a que la
llamada X a Objeto uno termine antes de pedir la llamada Y a objeto dos. Esto es muy
lento, pero no requiere ninguna programación en especial en el servidor, porque nos
garantiza que solo una llamada va a llegar desde COM a la vez.
Apartment Threaded
Bajo este modelo, COM crea un hilo de ejecución por cada objeto, y solo ejecuta una
llamada a la vez por objeto (por ejemplo, si dos hilos de ejecución tratan de llamar métodos
del mismo puntero de interfase a la vez, COM los llama uno por uno). Obviamente esto sólo
tiene sentido en Multiple Instance. Como hay un hilo por cada objeto, debemos tener
cuidado cuando utilizamos variables globales (Printers, Screen.DataModules,
Screen.Forms), para evitar accesar memoria que otro de los hilos ha liberado y cosas así.
Free Threaded
Bajo este modelo, los clientes pueden llamar a cualquier método de un objeto desde
cualquier hilo de ejecución a cualquier momento. Los servidores con modelo de Free
Threaded son los más difíciles de desarrollar porque deben de proteger no solo las
variables globales al ejecutable, sino también las variables locales al objeto (porque otro
hilo podría llamar a otra función del objeto que cambia la misma variable). Esto permite
mucha facilidad a los clientes, porque los clientes pueden compartir un sólo puntero de
interfase para todas sus operaciones y no preocuparse de nada.
Both
Este modelo es un modelo que permite a clientes utilizar Apartment y Free a la vez.
El mundo de COM es muy grande, y apenas hemos visto suficiente para poder comenzar a hacer
un ejemplo y entenderlo. Si este tema le interesa, le recomiendo que compre un buen libro acerca
de COM/DCOM y se asome al código fuente de los objetos de Delphi en comobj.pas, ComServ.pas
y ActiveX.pas. Espero que esta explicación le haya dado suficientes bases para entender
conceptos más avanzados.
Ahora que sabemos cómo funciona una interfase y como se implementa en Object Pascal, veamos
cómo funcionan las interfases COM (y DCOM) en específico.
COM es un transporte para interfases. La conveniencia de COM es que viene con todas las copias
de Windows en el planeta, así que no hay que preocuparse de instalar librerías con su sistema. La
parte inconveniente es que si usted utiliza COM, su programa solo puede hablar con máquinas que
corran Windows (hay un esfuerzo para implementar la librería COM en Unix, pero no ha tenido
éxito técnico o político).
Ejecutables en Windows
La implementación de una interfase COM en Delphi puede vivir en cualquier archivo de 32 bits que
Windows pueda ejecutar. Estos archivos pueden ser ejecutables, Librerías DLL o Librerías OCX.
Hay ciertos detalles en cuanto a lo que es soportado en cada uno de estos tipos de
implementación, como hilos de ejecución o capacidad de funcionar como un control de ActiveX. Un
solo ejecutable puede implementar todas sus interfases, o puede usted granularizar en diferentes
servicios como más convenga a su caso en particular.
Para poder instanciar una interfase, la computadora necesita saber en qué ejecutable vive esta
interfase, y además necesita saber algunos otros detalles acerca de su ejecución, como puntero
de entrada a cada una de las interfases (dentro del ejecutable), permiso de usuario con los cuales
el ejecutable va a funcionar, etcétera. Toda esta información es guardada por el registro de
Windows.
Lo primero que necesitamos para una interfase es no confundirla con otra. Para esto se utilizan los
llamados GUIDs, o Globally Unique IDentifiers. Estos identificadores son una especie de número de
serie aleatorio de 128 bits generado utilizando una función llamada CoCreateGUID. La función está
escrita de manera tal que se le garantiza que éste numero nunca será repetido. Esto es
importante porque el GUID es el identificador que la computadora utilizará para nuestra interfase
en todas las computadoras de todo el mundo, y no queremos que nuestras interfases se
confundan con las de otra persona. Por ejemplo, en todo el planeta, el siguiente GUID identifica al
Active Desktop de Internet Explorer:
{FBF23B40-E3F0-101B-8488-00AA003E56F8}
Tómese unos cuantos minutos para examinar el registro de Windows con el programa REGEDIT.
Usted podrá observar que uno de los folders de "raíz" se llama HKEY_CLASSES_ROOT. Este folder
contiene toda la información de todas las interfases. Su nombre, su identificador único, sus
permisos, etcétera. Windows utiliza COM durante su operación diaria, así que usted encontrará los
GUIDs de interfases de Windows, Borland, y todas sus aplicaciones junto con sus propias
interfases.
Utilizar regedit para ver sus interfases puede resultar complicado. Microsoft tiene funciones para
averiguar información acerca de las interfases sin tener que accesar el registro, y se reservan el
derecho de cambiar el formato del registro. Así que dentro de lo posible, procure que su programa
utilice las funciones del Windows API para averiguar información acerca de las interfases en vez de
usar el registro.
Instanciando Interfases
Los programas en Windows pueden ser muy diferentes. ¿Cómo sabe Windows cómo instanciar una
interfase? Cuando el programa inicializa (antes de comenzar a ejecutar), Delphi crea por nosotros
una "Fabrica de Objetos" (Class Factory). Una fábrica de objetos es un objeto concreto que genera
objetos de la clase cada vez que Windows decide que un programa cliente ha solicitado un
puntero de interfase. Si usted utiliza las capacidades de RAD de Delphi, rara vez se tendrá que
preocupar acerca de esto, pero es importante que tenga en mente como funciona para resolver
algunos problemas que no son obvios. Por ejemplo, si por algún motivo su objeto nunca inicializa y
usted nota que el procedimiento create nunca ejecuta (y el objeto está registrado), el problema
puede ser que la fábrica de objetos nunca está siendo creada. Si no hay fábrica de Objetos,
Windows no puede crear una CoClass.
implementation
uses ComServ;
initialization
TAutoObjectFactory.Create(ComServer, TClientes, Class_Clientes,
ciMultiInstance, tmApartment);
end.
TAutoObjectFactory es una fábrica de clases que genera objetos COM. El Create crea una nueva
fábrica de clases y la añade al ComServer (un objeto global dentro de la unidad ComServ que
mantiene una lista de todas las fábricas de clases en este ejecutable). También registra el hecho
de que va a instanciar objetos de Delphi TClientes cada vez que Windows necesite una CoClass
llamada Class_Clientes (esta CoClass fué generada por la biblioteca de tipos del proyecto
automáticamente, y tiene asociado un GUID para ponerlo en el registro de Windows. Vea el
capítulo práctico para entender como funciona con un ejemplo).
También notará que el Create le notifica a la fábrica de clases que este objeto es de instancia
múltiple y su modelo de hilos de ejecución es "Apartamento".
Por ejemplo, las siguientes gráficas ilustran cómo hace COM para instanciar un objeto a partir de
una fábrica de Clases. Cuando un cliente pide la interfase IClientes (1), COM busca en el registro
de Windows el GUID que corresponde a IClientes (a menos que usted haya especificado el GUID en
vez del nombre), y después busca el ejecutable que corresponde al GUID. Si el archivo no está en
memoria, lo ejecuta (2). Una vez hecho esto, COM busca en la lista de la librería de clases (que
Delphi guarda en el objeto global ComServer) la fábrica de clases (en rojo) que puede crear
IClientes (representado por Class_Clientes en el código fuente arriba) (3).
Después, COM llama al método de la fábrica de clases para crear un objeto, y la fábrica de clases
crea un objeto TClientes (4) (usando el método TComObject.CreateFromFactory) y asigna un
puntero de interfase, el cual es devuelto a COM (5).
COM a su vez crea un puntero de interfase interno para devolver al cliente (6).
¿Porqué el paso 6? Recuerde que en un ambiente de memoria protegida como es Win32, usted no
puede accesar memoria que no le pertenece a su aplicación, así que COM necesita crear un
puntero para usted. Esto quiere decir que usted no puede comparar los numeros de puntero en el
lado del servidor y del cliente y esperar que sean el mismo. Esto también es util cuando
instanciamos un objeto usando DCOM en otra computadora (donde la posición de memoria no
tiene sentido en nuestra máquina).
Usted notará que lo complicado en COM ocurre en el servidor, no en el cliente. Lo único que el
cliente tiene que hacer es llamar CreateComObject para obtener un puntero de interfase y usarlo.
Toda la traducción y lo "Feo" se encuentra en el lado del servidor.
Single Instance
En este modelo, la primera vez que un cliente pide un puntero de interfase, la fábrica de
objetos crea nuestro objeto. Pero la segunda vez, la fábrica de objetos simplemente
encuentra la dirección de nuestro primer objeto y da esta dirección al cliente en vez de
crear un nuevo objeto. De esta manera usted tiene la garantía de que sólo un TCliente
estará en memoria dando servicio a todos los programas clientes.
Multiple Instance
Bajo este modelo, cada vez que un cliente pide un nuevo puntero de interfase, la fábrica de
objetos crea un nuevo objeto. Bajo este modelo usted debe tener cuidado con los nombres
de sus objetos para no confundirse, porque usted literalmente tendrá varios objetos
TCliente rondando por ahí (uno por cada cliente).
Single
Bajo este modelo, hay solo un hilo de ejecución. Todos los programas esperan a que la
llamada X a Objeto uno termine antes de pedir la llamada Y a objeto dos. Esto es muy
lento, pero no requiere ninguna programación en especial en el servidor, porque nos
garantiza que solo una llamada va a llegar desde COM a la vez.
Apartment Threaded
Bajo este modelo, COM crea un hilo de ejecución por cada objeto, y solo ejecuta una
llamada a la vez por objeto (por ejemplo, si dos hilos de ejecución tratan de llamar métodos
del mismo puntero de interfase a la vez, COM los llama uno por uno). Obviamente esto sólo
tiene sentido en Multiple Instance. Como hay un hilo por cada objeto, debemos tener
cuidado cuando utilizamos variables globales (Printers, Screen.DataModules,
Screen.Forms), para evitar accesar memoria que otro de los hilos ha liberado y cosas así.
Free Threaded
Bajo este modelo, los clientes pueden llamar a cualquier método de un objeto desde
cualquier hilo de ejecución a cualquier momento. Los servidores con modelo de Free
Threaded son los más difíciles de desarrollar porque deben de proteger no solo las
variables globales al ejecutable, sino también las variables locales al objeto (porque otro
hilo podría llamar a otra función del objeto que cambia la misma variable). Esto permite
mucha facilidad a los clientes, porque los clientes pueden compartir un sólo puntero de
interfase para todas sus operaciones y no preocuparse de nada.
Both
Este modelo es un modelo que permite a clientes utilizar Apartment y Free a la vez.
El mundo de COM es muy grande, y apenas hemos visto suficiente para poder comenzar a hacer
un ejemplo y entenderlo. Si este tema le interesa, le recomiendo que compre un buen libro acerca
de COM/DCOM y se asome al código fuente de los objetos de Delphi en comobj.pas, ComServ.pas
y ActiveX.pas. Espero que esta explicación le haya dado suficientes bases para entender
conceptos más avanzados.
En el capítulo 9, vimos la teoría de las interfases en general. Ahora que entendemos la idea,
veamos cómo CORBA soluciona el problema. Si usted decidió leer el capítulo 9.1, para ahora
tendrá una muy buena idea de como funcionan las interfases en Microsoft COM y podrá usted
darse cuenta de las diferencias tanto de filosofía como de implementación.
En 1989, los altos ejecutivos de varias compañías (desde compañías de Software hasta Bancos) se
reunieron para hablar acerca de interfases, y de la importancia que éstas tendrían en el futuro.
Estas empresas (que forman el llamado "Open Management Group") decidieron que, dada la
enorme importancia que las interfases tendrían, sería muy util especificar un estándar para que
todos los lenguajes de programación pudieran exponer interfases, sin importar el lenguaje, el
sistema operativo o el vendedor de servicios de objetos.
CORBA es el resultado de muchos años de trabajo de varias compañías para definir un estándar
que satisfaga a todos los lenguajes de todos los sistemas operativos. COM fué el primer transporte
de interfases soportado por Delphi, pero CORBA es una tecnología mucho más madura y sólida
para tranporte de interfases, por el simple y sencillo motivo de que CORBA ha estado funcionando
por mucho tiempo en varios sistemas operativos y lenguajes.
Así que la primera diferencia entre COM y CORBA es que COM nos limita a tecnologías Windows,
mientras CORBA nos abre las puertas a la posibilidad de usar e implementar interfases en Unix,
Linux, Mainframes de IBM, Solaris, Sillicon Graphics, Windows, etcétera, en lenguajes tan dispares
como Java, C, Delphi, COBOL, SmallTalk, FORTRAN, LISP y muchos otros.
Independencia de Lenguaje
¿Cómo se logra la independencia entre lenguajes? Tal como vimos en el capítulo 9, Las interfases
CORBA se representan en un Lenguaje unificado llamado IDL (Interface Definition Language). Cada
lenguaje (Delphi, Java, C, etc.) cuenta con un traductor (también llamado "compilador") de IDL,
que traduce las sentencias de la definición de la interfase al lenguaje adecuado.
Cuando usted compra librerías de CORBA (también llamadas ORBs) para su lenguaje, las librerías
normalmente vienen con programas llamados "idl2XXX", donde "XXX" es el nombre de su
lenguaje. De esta manera, un ORB para java viene con un compilador de IDL llamado "idl2java",
mientras que C++ vendría con un compilador llamado "idl2cpp". En delphi tenemos un caso
especial, porque Delphi puede leer IDL directamente en el editor de librerías de tipos y traducirlo a
PAS automáticamente (así que el compilador de IDL viene integrado en las versiones Client/Server
y Enterprise).
Como ORB no es dominado por una sola compañía sino por un grupo, mientras uste pueda
compilar su programa para un sistema operativo X y pueda comprar unas librerías ORB para el
mismo sistema operativo (y que funcionen con su lenguaje), usted puede hablar con cualquier otro
servicio en la red que exponga un ORB, sin importar qué sistema operativo o qué marca de ORB
está ejecutándose en la máquina que expone el ORB.
Tal como lo hicimos en las interfases, hay que ver lo que CORBA no es:
CORBA NO ES un producto
CORBA es una especificación de como debe funcionar un set de librerías específicas para
llamarse un ORB. Decir "yo sé usar CORBA" no es suficiente; usted normalmente debe decir
cosas como "yo sé CORBA usando Visigenics", o "yo sé CORBA usando IONA". Visigenics e
IONA son dos productos que implementan la tecnología CORBA. Los productos que
implementan CORBA se llaman ORBs.
CORBA NO ES un lenguaje
De la misma manera, Visigenics (un ORB) está disponible para varios lenguajes bajo varias
plataformas (Visigenics for C++/Linux, Visigenics for C++/Windows, Visigenics for Java).
Pero CORBA en sí mismo no es un lenguaje, sino una serie de librerías y servicios. Así que
recuerde que cuando explique a otra persona que usted puede programar en CORBA,
recuerde que debe decirlo de forma "Yo programo CORBA usando Visibroker para Delphi
bajo Windows", o "Yo programo CORBA usando Visibroker para Java", etcétera.
Componentes de Visibroker
Visibroker es un ORB que cumple con todos los requerimientos de CORBA. Visibroker se compone
de lo siguiente (Los nombres de servicio entre comillas son los nombres de la implementación de
Visibroker en particular):
Librerías de CORBA.
Las librerías de CORBA le ayudan a los programas a exponer métodos CORBA y a utilizar
los métodos desde los programas Clientes.
1. ORB
El ORB es el exportador e importador de interfases. Todos los clientes y servidores
deben inicializar el ORB y utilizarlo para obtener interfases. Básicamente el ORB es
el que interpreta los punteros de interfases y los traduce a mensajes de red, o
recibe llamadas de red y los traduce a punteros locales para su ejecución.
El servicio SmartAgent le ayuda a exportar un objeto para su utilización en una red local. Si
usted tiene el código de interfase I.O.R. en específico, usted no necesita un servicio de
nombres.
Cuando usted escribe un programa COM que exporta interfases, Windows utiliza uno de sus
servicios internos (TODO:Averiguar Nombre del Servicio) para ejecutar el archivo (.exe, .dll,
etc) donde la interfase "vive". CORBA no es una tecnología que dependa en el sistema
operativo, así que el servicio de activación tiene una lista de las interfases y los nombres
de sus ejecutables para poder iniciar automáticamente el ejecutable. Note que si usted
comienza el ejecutable por sí mismo, no necesita registrarlo en el OAD. De esta manera,
usted puede tener varios servicios en su AUTOEXEC.BAT (o lista de servicios en Windows
NT) que aceptarán peticiones de los clientes.
Compilador de IDL
IDL es un lenguaje "neutral" para definición de interfases. Como tal, tiene declaraciones
pero no tiene sintaxis de control (for, do while, etc). Bajo CORBA, usted describe su
interfase utilizando IDL y utiliza el compilador de IDL para crear clases de apoyo a su
implementación, y clases auxiliares para que los clientes puedan crear instancias del
objeto.
Visibroker y Delphi
Como podrá ver, Delphi 4 utiliza una versión de Visibroker para C++. Como es el caso con
todas las librerías de C++, las llamadas han sido traducidas a archivos .PAS para su uso en
Delphi.
Capitulo 9.4. Mezclando Lenguajes y Plataformas con CORBA: Cómo Platicar con
programas en Java
En éste capítulo veremos cómo podemos hacer que Delphi (Enterprise) y Java platiquen en
CORBA. Además le servirá de práctica acerca de cómo hacer un programa en CORBA en Delphi sin
utilizar MIDAS.
Este projecto utilizará dos programas (cliente y servidor) que estarán implementados en dos
lenguajes (Delphi y Java). Como este es un curso de Delphi nos enfocaremos más a Delphi en
cuanto a los detalles.
La idea de hacer dos clientes y dos servidores tiene por objeto demostrar que podemos llamar al
servidor de Java desde un cliente de Delphi y viceversa. Además, ver dos ejemplos en lenguajes
diferentes le permitirá entender mejor hasta donde termina Delphi y donde comienza CORBA.
Como esta será una interfase muy simple, utilicemos Delphi para hacer un servidor. Simplemente
creamos una nueva aplicación (le he cambiado el Título a la forma para que diga "Servidor de
CORBA super-básico" y después seleccionamos File-New.
implementation
uses CorbInit;
initialization
TCorbaObjectFactory.Create('ObjetoCursoFactory',
'ObjetoCurso', 'IDL:Project1/ObjetoCursoFactory:1.0', IObjetoCurso,
TObjetoCurso, iSingleInstance, tmSingleThread);
end.
En COM todos los objetos deben contar con una fábrica, pero en CORBA
éste no es el caso (en CORBA una fábrica es simplemente una técnica
específica de implementación, que no es requerida). Pero como Delphi
utiliza el mismo código para generar objetos en COM y CORBA, mientras
no implemente su servidor manualmente (con todas la complicaciones
que esto genera), Delphi siempre creará una fábrica (llamada
SuObjetoFactory) por usted. Esto es importante tener en cuenta cuando
utilice otros lenguajes para conectarse a servidores en Delphi, como
veremos más adelante.
Ahora que tenemos listo nuestro código inicial, comencemos por crear algunos métodos. Grabe su
proyecto (yo grabé mi DPR "ORBDelCurso"), y a continuación seleccione "View-Type Library". El
editor de librerías de tipos aparecerá a continuación. Notará que el editor de tipos es el mismo al
mostrado en el Capítulo 6.6.
Utilice el botón con flecha verde para crear un nuevo método, y nómbrelo "DeCabeza". Lo que
queremos hacer es una función que devuelva la cadena que ha recibido pero al revés.
TuQuienEres no tiene parámetros, pero necesitamos declarar uno (out, retval) para satisfacer al
editor de Tipos, que como es menso :-) cree que está lidiando con un IDispatch de COM y necesita
que todas las funciones regresen un HResult (lo cual a nosotros, como programadores de CORBA,
no nos interesa).
implementation
uses CorbInit;
end;
end;
Note como, aún cuando la librería de tipos nos hace especificar y dar nombre a parámetros de
salida y nos obliga a especificar HRESULT como el tipo de retorno, nos ha definido las funciones
correctamente (regresando WideString). Esto es porque aunque el editor de tipos está enfocado a
COM, Delphi sabe cómo escribir CORBA adecuadamente. ¡Además esto nos permite exportar el
objeto como objeto COM y convertirnos en un servidor COM y CORBA a la vez, cosa que ningún
paquete hasta ahora ha permitido hacer!
Result := '';
// Este for invierte los caracteres del mensaje
for i := Length(Cadena) downto 0 do Result := Result + Cadena[i];
end;
Para que nuestros clientes de Java se comuniquen con nosotros necesitaremos generar la
especificación de nuestra interfase en lenguaje IDL. Para hacer esto utilizamos el botón de la
esquina derecha en la barra de botones de la librería de tipos ("Export to IDL"). Éste botón exporta
a COM IDL, pero tiene un menú junto al mismo (subrayado por la franja roja en la gráfica
siguiente) que le permite seleccionar qué tipo de IDL quiere exportar. Selecciónelo y elija "Export
to CORBA IDL".
interface IObjetoCurso
{
wstring DeCabeza(in wstring Cadena);
wstring TuQuienEres();
};
interface ObjetoCursoFactory
{
IObjetoCurso CreateInstance(in string InstanceName);
};
};
Grabe este archivo (he llamado al mío "OrbDelCurso.idl") Cuando hagamos nuestro cliente (y el
servidor) en Java, necesitaremos el archivo para que Java nos genere el código apropiado (con la
utilería "idl2java").
Si usted genera IDL manualmente o con algún ORB escrito en otro lenguaje, necesitará utilizar una
utilería equivalente para generar el código "pas".
Ya que tenemos un servidor CORBA, podemos crear nuestro cliente. Como de costumbre, el
cliente es más sencillo (en cuanto a la conexión de objetos) que el servidor. Para crear nuestro
cliente sólo tenemos que incluir el archivo "OrbDelCurso_TLB.pas", que es nuestra especificación
de la interfase en Pascal.
Comenzaremos por crear una simple interfaz de usuario con un EditBox y tres botónes, uno para
conectar/desconectar, otro para llamar la función DeCabeza y otro para averiguar quién es
nuestro servidor. También pondremos un memo para las respuestas.
Ahora, hacer el cliente es sencillísimo. Seleccione "Project-Add to Project" del menú de Delphi y
navegue al directorio donde hizo el servidor. Añada el archivo "ORBDelCurso_TLB.pas" a su
proyecto. Añada ORBDelCurso_TLB a su clausula de USES en la sección "interface" de su forma
principal. Ahora, dentro de la forma del cliente (ya sea en private o public) declare una variable de
tipo IObjetoCurso (el mío se llama ObjetodelCurso).
ObjetodelCurso := TObjetoCursoCorbaFactory.CreateInstance('');
Donde desee hacer la conexión. A partir de entonces, su variable será un objeto al
cual usted puede llamar como cualquier otro objeto de Delphi, como por ejemplo
este código, que pone el resultado a la llamada remota "DeCabeza" en el Memo,
utilizando como parámetro nuestro campo de edición:
// De Cabeza
Memo1.Lines.Add(ObjetoDelCurso.DeCabeza(Edit1.Text));
Esto quiere decir que usted puede ejecutar una o varias veces su servidor en alguna computadora
de su red TCP/IP y sus clientes lo encontrarán "mágicamente", sin necesidad de mencionar una
computadora.
Para poder ejecutar el servidor, usted necesitará tener el servicio SmartAgent ejecutando. Si su
servicio SmartAgent no está presente o no está configurado al mismo puerto TCP que su objeto
CORBA (vea el "Smart-Agent Reg-Edit tool" en su folder de Visibroker en el menú de inicio), el
programa no ejecutará (porque Delphi se quedará esperando a que un servicio de nombres
Visibroker conteste).
Así que, ejecute su "Visibroker Smart Agent" (que es el registro de objetos de Visibroker). Se
encuentra en el menú de inicio, en el Subgrupo de "Visibroker" en su Delphi 4 o 5 Enterprise.
SmartAgent muestra una ventana blanca sin ningún mensaje.
He aquí una imagen de mi cliente en acción (mi servidor también está funcionando):
Un Cliente CORBA en Java
A continuación veremos cómo implementar un cliente en Java. Como este curso no está enfocado
a Java, sólo explicaré cómo generamos nuestro código con idl2java, como conectarnos al servidor,
y cómo accesamos nuestro objeto desde Java, sin explicar gran cosa acerca de como compilar,
cómo funcionan los botónes, etcétera. Pero he incluido en el Zip el código fuente en Java también,
para que usted lo pueda ver y examinar.
Como mencionamos con anterioridad, primero debemos convertir el IDL que generamos al crear el
servidor de Delphi al lenguaje que usaremos para conectarnos, en este caso Java. Así que
ejecutamos "idl2java ORBDelCurso.idl" en la línea de comando. En el caso de IDL2Java, nos
generará un directorio con el nombre del módulo de CORBA, en este caso "ORBDelCurso". Este
directorio es un paquete de Java al que podemos hacer referencia en nuestro código.
Como hemos visto con anterioridad, los clientes son más fáciles de implementar que los
servidores. Ahora que tenemos el paquete que nos dice cómo pedirle a CORBA que utilice el
objeto sólo tenemos que crear un ObjetoCursoFactory y pedirle que nos genere un ObjetoCurso.
Idl2Java ya nos ha escrito este código, y lo ha puesto en la clase ObjetoCursoFactoryHelper.
Así que, para implementar un cliente que utilice el objeto que hemos escrito en Delphi, debemos
instanciar un objeto con el FactoryHelper, y después llamar a CreateInstance en el objeto (por
aquello de que Delphi siempre usa Fábricas de clases) para obtener nuestro objeto del curso. Por
ejemplo:
ORB orb;
ObjetoCursoFactory fac;
IObjetoCurso obj;
Inicializa el orb con ORB.init, y los posibles argumentos de la línea de comando (solo lo
tiene que hacer una vez, al comenzar la aplicación).
Pide al SmartAgent que nos encuentre una implementación de la fábrica de clases (porque
Delphi publica la fábrica de clases, no el objeto).
Llama a CreateInstance para que nos dé un puntero de interfase al objeto que queremos
usar. Delphi recibe esta llamada y crea un TObjetoCurso, devolviéndonos la interfase que
queremos. Ahora obj tiene un IObjetoCurso que podemos llamar en Java como si fuera un
objeto local.
He creado un pequeño cliente en Java que usted puede ejecutar si tiene Visibroker for Java o
JBuilder utilizando la línea de comando "vbj ClienteORB usabind".
Ahora que hemos probado que nos podemos comunicar entre Delphi y Java, tratemos de crear un
servidor en Java, para tener las cuatro piezas y poder examinar cómo funciona todo esto.
Hé aquí el código:
La fábrica sólo tiene una función, que se llama CreateInstance. Delphi nos había ahorrado el tener
que implementar el CreateInstance, pero como estamos en java lo debemos hacer manualmente.
Afortunadamente es muy fácil:
Obviamente, ahora necesitamos hacer un ObjetoCurso.java que responda a las peticiones del
cliente. Idl2Java también nos ha escrito una base que podemos extender, así que simplemente
hacemos una clase que extienda _IObjetoCursoImplBase e implemente nuestras funciones
"DeCabeza" y "TuQuienEres".
Ahora lo podemos ejecutar y pedir al cliente de Delphi (o al de Java) que se conecte. Para
ejecutarlo las reglas son las mismas que con Delphi. El SmartAgent tiene que estar ejecutando.
Usted puede ejecutarlo con la línea de comando "vbj ObjetoCursoServidor".
Con todo esto, apenas hemos tocado la punta del iceberg en cuanto a CORBA y Visibroker. Espero
que esto le haya picado la curiosidad de investigar el gran mundo de la computación de grande
empresa.