Delphi, OOP
Delphi, OOP
2 Introduccin a
Herencia de Clase
Conceptos Principales:
Herencia de clase Subclase para reutilizacin: extensin, especializacin, generalizacin Solapando mtodos heredados Encapsulacin Visual Form Inheritance (VFI) Jerarqua de clase del VCL de Delphi Navegacin (enlazado) entre objetos Navegacin sobre un diagrama de clase UML Visibilidad para paso de mensaje
Pgina 1 de 19
Creado el 16/04/09
Introduccin
En el captulo 1 hablamos sobre tres perspectivas sobre objetos: objetos como entidades independientes, objetos como entidades derivadas y objetos como entidades que interactan. En ste captulo realizaremos una primera aproximacin a los objetos como entidades derivadas usando una de las facilidades del RAD de Delphi, llamada Visual Form Inheritance (VFI). Esto nos permite definir un formulario y luego usarlo como la base de futuros formularios a travs de la herencia. Utilizaremos VFI para reutilizar un formulario existente a travs de la herencia y luego ampliaremos y especializaremos estos formularios derivados a travs de nuestro cdigo. Y ya que los objetos de forma aislada no tienen mucha utilidad, analizaremos brevemente la interaccin entre estos objetos.
Pgina 2 de 19
Creado el 16/04/09
Haga el form de un tamao conveniente (sobre 200 x 100) y cree un manejador para el evento OnClick del botn button1 en Unit2:
13 implementation 14 {$R *.dfm} 15 16 17 18 19 procedure TForm2.Button1Click(Sender: TObject); begin ShowMessage ('Button ' + (Sender as TButton).Caption + ' clicked'); end; // end procedure TForm2.Button1Click end. // end Unit2
Los forms descendientes (TForm3 y TForm4 en la Figura 1) heredern ste mtodo, y usaremos el parmetro Sender para identificar el Caption del botn que ha sido pulsado. La lnea 17 es la primera aparicin del parmetro Sender y lo veremos unas cuantas veces ms a lo largo de ste mdulo.
Para crear TForm3 en Delphi 4/7, seleccione la secuencia de men File | New | Other y luego seleccione la pestaa denominada Project1 (o Project2, 3, ...., dependiendo del nombre de su proyecto actual). Hasta el momento, todos sus formularios han sido derivados desde TForm. Debido a que estamos usando TForm2 como base, Delphi deriva nuestro nuevo formulario, TForm3 en Unit3, desde TForm2 (lnea 7 anterior). Las Units se encapsulan las unas de las otras, y as para que la Unit3 pueda referirse a TForm2 en la declaracin de clases (lnea 7), Delphi inserta Unit2, el cual define TForm2, en la clusula global uses de la (lnea 5) Unit3.
1 unit Unit3; 2 interface 3 uses 4 Windows, Messages, SysUtils, Variants, Classes, Graphics, 5 Controls, Forms, Dialogs, Unit2; 6 type 7 TForm3 = class(TForm2) // hereda desde TForm2, no TForm 8 private 9 { Private declarations } 10 public 11 { Public declarations } 12 end; 13 var 14 Form3: TForm3; 15 implementation 16 {$R *.dfm} 17 end.
Pgina 3 de 19
Creado el 16/04/09
hereda todos los campos de datos (propiedades) y caractersticas (mtodos y manejadores de eventos) desde TForm2, as que un botn, definido en TForm2, aparece como parte de la visualizacin del objeto Form3 (Figura 2) aunque no figure en la definicin de tipos de TForm3 (lneas 712). Usando el Inspector de Objetos, cambie el Caption del botn a btnForm3 y la posicin del form (propiedades Top y Left) para que no quede tapado por los dems formularios. No haga ms cambios sobre Form3 de momento.
TForm3
Pgina 4 de 19
Creado el 16/04/09
La convencin UML usa puntas de flecha cerradas y sin relleno para herencia y puntas de flecha abiertas para enlaces de navegacin (tambin llamados enlaces de asociacin). La Figura 3 muestra puntas de flecha abiertas en los enlaces entre TForm1, TForm3 y TForm4, apuntando desde TForm1 a los otros dos forms. Esto refleja que Form1 puede acceder a Form3 y a Form4, pero no hay acceso disponible en la direccin inversa. Para implementar estos enlaces, retorne a la Unit1. Usando la paleta Component / Tool, coloque un botn en TForm1. Establezca su propiedad Name a btnShowForms y su Caption a Show Forms. Cree un manejador para el evento OnClick que enve mensajes a Form3 y Form4, haciendo que se muestren asmismos (lneas 2021). Presione <F9> para ejecutar el programa. Delphi mostrar una ventana de informacin, diciendo: Form Form1 references Form3 declared in Unit3 which is not in your USES list. Do you wish to add it? Seleccione Yes como respuesta. Ejecute de nuevo. Repite la pregunta, pero sta vez para la Unit4. Pulse Yes para aadir Unit4 a la clusula uses tambin. Delphi aade una clusula local uses Unit3, Unit4; a Unit1 (p.ej., en su implementacin privada y no en la pblica, seccin de interfaz) (lnea 16 a continuacin).
15 implementation 16 uses Unit3, Unit4; 17 {$R *.dfm} 18 procedure TForm1.btnShowFormsClick(Sender: TObject); 19 begin 20 Form3.Show; 21 Form4.Show; 22 end; // end procedure TForm1.btnShowFormsClick
Ejecute de nuevo el programa. El Form1 (slo l) aparece. Pulse en el botn para invocar al manejador del evento OnClick y Form3 y Form4 aparecern. Form2 no aparece porque no ha sido instrudo a mostrarse a s mismo. Un click sobre Form1 puede enviar un mensaje Show a Form3 y Form4 porque hay una asociacin entre TForm1 y TForm3 y entre TForm1 y TForm4 (Figura 3). ste enlace es posible gracias a la clusula uses en la Unit1, la cual lista Unit3 y Unit4 (lnea 16 anterior).
Pgina 5 de 19
Creado el 16/04/09
Pulse el botn en Form3 o Form4, y un mensaje identificando el botn por su Caption aparece (Figura 4).
Ni TForm3 ni TForm4 tienen definidos campos de datos ni operaciones. Sin embargo ambos estn derivados de TForm2, el cual define un campo de datos TButton y su manejador de eventos OnClick, y as tanto TForm3 como TForm4 heredan el botn y su manejador de eventos desde TForm2. As, a travs de la herencia, TForm3 y TForm4 reutilizan TForm2 y en ste caso no necesitan ningn cdigo propio. Establecemos distintos Caption para los botones en Form3 y Form4 para mostrar que est funcionando el cdigo con diferentes objetos. Para identificar estos objetos usamos el parmetro Sender en la lnea 17 del manejador de eventos del evento OnClick del Form2 (ejemplo 2.1, paso 2). Ya que cada instancia tiene su propio dato, Form3 y Form4 tambin tienen sus propios valores para Top, Left, etc, y es por esto por lo que pueden ocupar posiciones distintas en la pantalla. Almacene ste proyecto, ya que lo usaremos como punto de inicio para los ejemplos 2.2 y 2.3.
Pgina 6 de 19
Creado el 16/04/09
Reflejamos la navegabilidad en nuestro diagrama de clases UML a travs de puntas de flecha de cabeza abierta sobre los enlaces de asociacin (Figura 5). Aqu mostramos los atributos y mtodos relevantes que extienden la superclase. Para presentar un estilo ligeramente diferente, las rutas de herencia son mostradas como flechas separadas, y no como rboles grficos combinados como en los anteriores diagramas.
Existe una ligera diferencia aqu con respecto a nuestros anteriores manejadores de evento. Como TForm3 est derivado desde TForm2 (y no desde TForm), Delphi automticamente inserta el estamento inherited como primera lnea de programa en ste manejador de evento. Djelo por ahora aunque no pinte nada aqu -discutiremos el significado de inherited en el prximo ejemplo-. TForm3 ahora hereda un botn y un manejador de evento desde TForm2 y aade un CheckBox y otro manejador de evento por s mismo.
Pgina 7 de 19
Creado el 16/04/09
Aqu de nuevo, Delphi automticamente inserta el estamento inherited. Incluimos un mensaje desde Form4 a Form3 para limpiar el CheckBox cada vez que Form4 reposiciona Form1.
La subclase hereda toda la funcionalidad de la superclase, La subclase aade nueva funcionalidad, La subclase no hace cambios en la funcionalidad de la superclase (que hereda).
Pgina 8 de 19
Creado el 16/04/09
El subclaseado para extensin es lo que los programadores suelen tener en mente cuando se exaltan las virtudes de la herencia como aproximacin importante al problema de la reutilizacin. Tambin usamos ste ejemplo como una oportunidad para ver brevemente una navegacin bidireccional entre objetos y para ver la representacin de la navegacin en un diagrama de clases.
Ejecute ste programa. Pulsar el botn en Form3 nos muestra el mensaje original generado por el manejador de eventos de Button1Click de TForm2 (ejemplo 2.1, paso 2, lnea 17). La aceptacin de ste mensaje nos lleva entonces a otro mensaje generado por el manejador de eventos Button1Click de TForm3. Por contraste, la pulsacin del botn en Form4 nos da slo la funcionalildad derivada desde Form2. As que con TForm3 tenemos un caso donde usamos la herencia para aadir a la funcionalidad heredada, un proceso llamado especializacin.
Pgina 9 de 19
Creado el 16/04/09
Veamos qu est pasando aqu. Cuando hace doble click sobre el Button1 en Form3 durante el modo diseo, Delphi crea un nuevo manejador de eventos de Button1 como mtodo de TForm3 (declarado en la lnea 8 anterior). Para cualquier objeto de clase TForm3, el manejador de evento OnClick en TForm3 sobreescribir el manejador de eventos heredado en TForm2 (por contraste, TForm4 no declara su propio manejador de eventos, as que Form4 por tanto sube un nivel y ejecuta el manejador de eventos heredado desde TForm2). Delphi tambin genera el esqueleto del manejador de eventos (lneas 1416 y 18 arriba). Lo que difiere aqu de los esqueletos de manejadores de evento que vimos en el captulo 1 es que Delphi inserta un estamento inherited (lnea 16 anterior y tambin en los manejadores de evento del ejemplo 2.2). Por qu pasa esto? Normalmente, con la especializacin, nosotros queremos aadir alguna funcionalidad a la disponible desde la superclase. Sin embargo, en el prrafo anterior dijimos que el manejador de eventos en la clase derivada reemplaza completamente al manejador de eventos definido en la superclase. La palabra clave inherited en el mtodo de la subclase llama al correspondiente mtodo en la superclase. Por tanto el mtodo de la subclase sobreescribe completamente al mtodo de la superclase. Si todava deseamos que el mtodo (sobreescrito) de la superclase funcione, insertamos la palabra clave inherited, normalmente como primer estamento en el (sobreescrito) mtodo de la subclase. Habiendo llamado y ejecutado al mtodo de la superclase, ahora nos movemos a la lnea 17, la cual proporciona las operaciones de especializacin. Por inters, puede desear cambiar el orden de las lneas 16 y 17 y luego ejecutar el programa de nuevo. El mensaje desde el Button1Click de TForm3 ahora aparece antes que el de TForm.
Ejecute ste programa. Pulsando sobre el botn en Form4 reposicionamos Form1 con su propiedad top a 300 pixels sin displayar ningn mensaje. El manejador de eventos en la superclase no se ejecuta porque el manejador de eventos OnClick del Button1 de TForm4 (lneas 1518 anteriores) no contiene la palabra clave inherited y por tanto no se invoca al manejador de eventos OnClick del Button1 de la superclase (TForm2).
Pgina 10 de 19
Creado el 16/04/09
Cuando intentamos compilarlo, obtenemos dos mensajes de error. Aunque Unit1 tiene a Unit4 en su clusula uses, la Unit1 ve a Form4 como a un identificador no declarado porque Form4 est ahora declarado en la seccin de implementacin (privada) de Unit4 y no en su (pblica) seccin de interfaz. Para enfatizar que una declaracin en la seccin de implementacin es slo para el uso de la unit local, Delphi genera una advertencia de que la variable Form4 est declarada pero no es usada en Unit4. Restaure la declaracin de la variable a su lugar correcto antes de la palabra clave implementation y el programa funcionar correctamente de nuevo.
Pgina 11 de 19
Creado el 16/04/09
Aunque mostramos la herencia para extensin y la herencia para especializacin separadamente en los ejemplos 2.3 y 2.4, ambas suelen aparecer combinadas. Los programas OO tpicamente suelen incluir una sofisticada red de derivacin (a travs de la herencia y la composicin) y comunicacin (a travs de la asociacin).
Pgina 12 de 19
Creado el 16/04/09
La VCL1 proporciona los componentes (a travs de la paleta Component / Tool) para la construccin de la interfaz de usuario. Si miramos los componentes disponibles de la paleta Component / Tool, advertimos que todos tienen algunos grados de similaridad y diferencia. Comparando un TButton y un TLabel, vemos que existen similitudes entre ellos: ambos son visibles en un formulario en una posicin especfica. Pero tambin vemos grandes diferencias. Sin embargo, si comparamos un TButton y un TBitBtn vemos que las similitudes son mucho mayores. Cmo acomodamos distintos grados de similaridad y diferencia de forma efectiva? En principio, nos gustara codificar slo una vez todo aquello que es idntico en ambos componentes y usar nuevo cdigo exclusivamente para las caracteristicas adicionales en el nuevo componente. Los sistemas OO hacen esto posible a travs de la jerarqua de clase y la herencia. La Figura 8 muestra una muy simplificada representacin de la jerarqua de clases de la VCL de Delphi (un juego de clases de utilidades estndar mientras que la VCL tiene una extensiva y profunda jerarqua. Las jerarquas de clases que nosotros definimos son usualmente mucho ms simples).
En una jerarqua como sta buscamos los elementos comunes entre los distintos componentes y entonces codificamos stas igualdades en una (posiblemente artificial) superclase. Siguiendo sta aproximacin, tomamos todo lo que es comn entre un TLabel, un TButton y un TForm y lo codificamos en una nueva clase, TControl. Ahora vemos qu hay en comn entre TTimer, TControl y TMenu y lo codificamos en una nueva clase, TComponent, y as continuamos en la jerarqua. Nunca podremos instanciar las clases de nivel ms alto tales como TControl, TComponent o TPersistent. En su lugar proporcionamos la oportunidad de codificar caractersticas comunes una sla vez en un ancestro. Todos los descendientes entonces usan ste cdigo en lugar de escribirlo desde cero cada vez.
Delphi 6 y posteriores tambin contiene la librera CLX, introducida para portabilidad con Linux. La versin 8 incluye la librera .NET. Aqu slo trabajaremos con la original VCL que es comn en todas las versiones.
Pgina 13 de 19
Creado el 16/04/09
Mirando especficamente en la VCL, todo lo que es fundamental para todos los componentes y objetos aparece lo ms alto posible en la jerarqua, en la clase base. Todas las clases Delphi deben ser capaces de crear y destruir instancias de s mismas y por tanto TObject, el cual es la raz de la jerarqua de herencia de Delphi, tiene estas habilidades. Todos los objetos Delphi, excepto los objetos Exceptions y TStream, pueden ser guardados, y por tanto el siguiente nivel de la jerarqua separa los que pueden ser almacenados (TPersistent) de los objetos Exceptions y TStream. Algunas clases persistentes son componentes y otras no, y por tanto el tipo TComponent aparece bajo TPersistent en la jerarqua. TComponent introduce la habilidad de aparecer en la paleta Component / Tool y de ser manipulado en el Diseador del Form, y propiedades tales como Name. Tambin hereda la capacidad de ser almacenado desde TPersistent y de crear y destruir objetos desde TObject a travs de TPersistent. Algunos componentes son visuales, y otros, tales como TTimer o TMenu, no lo son. Los componentes visuales son visibles en tiempo de ejecucin y por tanto necesitan capacidades adicionales tales como Left y Top para dar su posicin en el formulario, los comentarios (propiedad hint) tipo pop-up y la habilidad de responder a acciones del ratn. Estas habilidades estn empaquetadas en TControl. Yendo ms abajo en la jerarqua finalmente llegamos a los actuales componentes que usamos en nuestros programas, tales como TLabel y TButton. Estos heredan todas las capacidades disponibles en TControl el cual a su vez hereda las capacidades de TComponent, el cual a su vez hereda todas las capacidades de TObject. Las habilidades comunes son codificadas en lo ms alto de la jerarqua, y estn disponibles para todas las clases inferiores que bajan por la jerarqua. Cada nuevo nivel en la jerarqua slo necesita codificar las habilidades adicionales y no necesita repetir lo que ya est disponible desde arriba. Por tanto, la caracterstica ms general es la que se codifica en el nivel ms alto de la jerarqua, y la ms especializada es la que se codifica en el nivel ms bajo de la jerarqua. Esto nos da pie a frases como generalizacin de la jerarqua y al trmino generalizacin que UML usa cuando se refiere a la herencia. Como vimos, TControl introduce propiedades para Left y Top, y por tanto l y todos sus descendientes tienen las propiedades Left y Top. Volviendo a la Figura 8, vemos que TControl, TLabel, TButton, TForm y TBitBtn tienen todos las propiedades Left y Top. Sin embargo, TMenu, TComponent y TObject, los cuales no son descendientes de TControl, no heredan las propiedades Left y Top.
Pgina 14 de 19
Creado el 16/04/09
Figura 10: Posicin de Ttimer en la jerarqua VCL tal como se muestra en la ayuda de Delphi 7
Si seleccionamos en enlace Properties, vemos una pantalla de las propiedades que TTimer declara por s mismo y aquellos que hereda desde TComponent (Figura 11). Similarmente podemos obtener una vista de los mtodos de TTimer (Figura 12). Esta es una lista mucho mayor que la de propiedades, y si bajamos por la lista veremos los mtodos de cada uno de sus ancestros.
Pgina 15 de 19
Creado el 16/04/09
Herencia Mltiple
Delphi, como Java y a diferencia de C++, no implementa la herencia mltiple. Para conseguir un efecto similar a la herencia mltiple en Delphi o Java, podemos usar la composicin y/o las interfaces. Volveremos a esto cuando veamos la composicin.
parte de la clase TfrmStructureDemo: TfrmStructureDemo TieneUn (HasA) Figura 3 del captulo 1 implica composicin. Como vimos en la Figura 5 del captulo 1, podemos redibujar ste diagrama para enfatizarla, usando una cabeza de diamante para indicar composicin (Figura 13 debajo). Usamos indicadores de multiplicidad sobre el enlace para mostrar que la clase TfrmStructureDemo est compuesta de dos botones Tbuttons.
En resumen, la construccin de una clase desde una o ms clases es llamada composicin. La filosofa detrs de la composicin es similar a la de la herencia -ser capaz de reutilizar objetos y clases existentes, y por tanto escribir cdigo slo para caracteristicas adicionales-. En OO, la composicin y la herencia son las mejores herramientas para crear nuevas clases y para la reutilizacin. Investigaremos la composicin en los prximos captulos.
Pgina 16 de 19
Creado el 16/04/09
Especializacin
Generalizacin
Pgina 17 de 19
Creado el 16/04/09
Objetos como entidades derivadas: Herencia de clase; composicin. Objetos como entidades que interactan: Mensajes simples, visibilidad de otras units y objetos. Diagramas UML: Herencia y asociacin en diagramas de Clases.
Pgina 18 de 19
Creado el 16/04/09
Problemas
Algunos de los problemas del captulo 3 comprueban principios cubiertos en ste captulo.
Cul es el significado de btNext? Cmo puede encontrar esto usando la ayuda? Cmo podra recodificar ste estamento If para que la parte del then en su lugar aada 20 y la parte else sustraiga 20 para dar la misma funcionalidad?
Pgina 19 de 19