<-- Capítulo

Índice del tutor de Delphi
© Copyright 1998
por David Martínez.

Todos los derechos reservados

Capítulo -- >

Capitulo 9. Teoría de Interfases

Introducción

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í.

Como en todos los conceptos abstractos, es muy importante también saber lo que una interfase NO es: Una interfase no es un objeto, tal como un rectángulo NO ES una caja de chocolates. El objeto (la caja de chocolates) soporta una implementación de la interfase (rectángulo). De esta manera, cualquier robot entrenado para manipular rectángulos puede manipular la caja de chocolates. De la misma manera, el módulo de acoplamiento del MIR no es una interfase, sino una implementación de la interfase. La interfase es la definición de como acoplarse al MIR (redondo, 2m diámetro, con tubos aquí y allá).

Todos los lenguajes modernos soportan el concepto de interfases. Hoy día en computación hay dos "lenguajes de transporte" para interfases: (COM y CORBA). Hablaremos de maneras específicas de crear una interfase con ambas tecnologías y de sus diferencias en los capítulos siguientes, pero por ahora sólo hablaremos de interfases de manera abstracta, con ejemplos prácticos.

Un uso Común para Interfases

Supongamos que su organización ha estado creciendo mucho últimamente. Todos los departamentos necesitan información de clientes y hay muchos desarrollos diferentes en su oficina. Todos los desarrollos requieren un catálogo de clientes, y usted hasta ahora ha resuelto el problema mediante un archivo de texto ASCII con la información de los clientes que su sistema de clientes pone en la noche del día 30 de cada mes y los otros programas lo importan.

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:

Si utilizaramos el diseñador de Delphi para generar esta interfase gráficamente, veríamos lo siguiente:

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:

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.

Imagine que tiene usted un sistema de TV por cable. Cuando usted enciende la televisión y pide el canal (HBO, por ejemplo), su caja de cable comienza a recibir el canal HBO mediante el pequeño cable que está enchufado a su pared. El punto en su caja de cable donde se enchufa el cable es el puntero de interfase mediante el cual viene HBO, pero no es HBO en sí. Sigo insistiendo en esto porque es muy importante que entienda que cuando usted pide una interfase esta recibiendo un puntero a la interfase que debe ser traducido por la máquina, y no el programa en sí.

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.

Si usted cree que esto se parece mucho a nuestra discusión de objetos en el Capitulo 3, tiene razón - Los conceptos son casi los mismos. Pero recuerde una vez mas que esta vez estamos hablando de la representación de los objetos, no de los objetos en sí mismo (como una analogía, piense que aquí estamos hablando de rectángulos como un concepto geométrico abstracto y anteriormente hablamos de objetos de forma rectangular - edificios, cajas de chocolates, étc).

Así que ya hemos visto cómo los clientes van a utilizar nuestra interfase. Pero nosotros, cómo la implementamos?

La interfase es una representación uniforme de un cliente para la organización. El programa que nosotros vamos a escribir debe "implementar" todas las funciones de la representación de un cliente. Usted puede escribir varios programas que representen al mismo cliente (i.e. varias "implementaciones" de la misma interfase, tal como una caja de chocolates y una caja de malvaviscos tienen todas cuatro lados y cuatro ángulos). Esto es cierto en cualquier lenguaje y en cualquier transporte.

Ahora vayamos a algo específico (al fin, algo de código, dirán algunos): ¿Cómo implementamos una interfase en el lenguaje Object Pascal?

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:

TODO: usar Pas2HTML en esta sección
  TXYZClientes = class(TInterfacedObject, IClientes)
   protected
   public
   private
   end;

En esta declaración, usted tiene un objeto TXYZClientes que hereda de un TInterfacedObject e implementa la interfase IClientes. Heredamos de TInterfacedObject porque todas las interfases necesitan saber hacer tres cosas: Añadirse al puntero de interfase, borrarse del puntero de interfase, y averiguar si hay alguna función o un procedimiento con un nombre específico en la interfase. Estas tres funciones también forman parte de una interfase, que se llama IUnknown. Veremos más información de IUnknown y de su pariente, IDispatch en la sección teórica de COM.

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:

  TXYZClientes = class(TInterfacedObject, IClientes)
  protected
     function Get_ClienteNo: LongInt;
     procedure Set_ClienteNo( Value: LongInt );
     function Get_Cliente : IPersona;
     procedure Set_Cliente( Value: IPersona );
     function Get_Direccion : IDirección;
     procedure Set_Direccion( Value:  IDirección );
     function GetTeléfono: ITeléfono;
     procedure Set_Telefono( Value: ITeléfono );
     function Get_ÚltimaCompra : Double;
     procedure Set_ÚltimaCompra( Value: Double );
     function Get_ÚltimoPago : Double;
     procedure Set_ÚltimoPago( Value: Double );
     { ... etcétera... }    
   public
  private
  end;

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.

Capítulo -- >