Delphi OOP-Cap 06

Embed Size (px)

Citation preview

Creado el 17/06/09

6 Introduccin a laHerencia de Tipos

Conceptos principales:

Sustitucin con clases de programador Enlazado temprano (esttico, tiempo de compilacin) y tardo (dinmico, tiempo de ejecucin) Mtodos virtual y override Polimorfismo = sustitucin + enlazado dinmico Mltiples asociaciones a travs de un nico enlace, reduccin de acoplamiento, evolucin Mtodos abstractos Concepto de tipo El patrn de polimorfismo

Aprendiendo OOP con Delphi

Pgina 1 de 24

Creado el 17/06/09

IntroduccinLa forma de herencia que concentramos en los primeros cuatro captulos es herencia de clase, o subclassing. El subclaseado o subclassing es una parte muy importante de la programacin orientada a objetos ya que proporciona un potente mecanismo para reutilizacin: las subclases reutilizan los campos de datos y mtodos de sus superclases a travs de la herencia. En el captulo 5 comenzamos a usar la herencia de forma diferente, para sustitucin. Una parte importante de la sustitucin es que las superclase establece un tipo particular. Las subclases que implementen ste tipo completamente, p.ej., aquellas que pueden sustituir las superclases bajo todas las condiciones, son llamadas subtipos. Esto puede no ser especialmente claro ya que en el ejemplo del captulo anterior utilizamos compontentes VCL, concentrados en la sustitucin. As que en ste captulo veremos de nuevo de forma breve la sustitucin, pero en el contexto de clases generadas por el programador. Entonces incorporaremos enlazado dinmico para extender la sustitucin al concepto del polimorfismo. Esto nos lleva de forma natural a una exploracin de clases abstractas. Los conceptos de sustitucin, polimorfismo y clases abstractas estn estrechamente vinculados al concepto de subtipado y cada una de stos tres conceptos son aspectos importantes de muchos patrones OO. Para algunas personas, el polimorfismo representa el corazn de la POO. Este captulo establece el concepto de polimorfismo (subtipado) de forma distinta a la de reutilizacin (subclaseado). Los siguientes captulos ilustran algunas de las formas en las que el polimorfismo es usado y revela por qu la hbil utilizacin del polimorfismo es uno de los aspectos ms importantes de la programacin OO.

Ejemplo 6.1. Un Programa PolimrficoComenzamos ste captulo revisando brevemente los principios desde el captulo anterior (p.ej., generalizacin y sustitucin), pero usando clases definidas por el programador, y luego extenderemos esto a un programa polimrfico introduciendo enlazado dinmico. Usamos una nica clase ancestro, TFurniture, y dos subclases , TChair y TTable (TChair y TTable son tambin subtipos vlidos del tipo TFurniture). Crearemos objetos de tipos distintos y luego les enviaremos un mensaje a cada uno llamando a un mtodo que responde a cada tipo. Para mantener los principios claros, ste es un ejemplo muy simple, y as estos varios objetos furniture slo sabrn de qu tipo son ellos. En un sistema completo, digamos para una tienda antigua, ellos podran ser ms complejos, con una variedad de mtodos y llevar informacin sobre su valor, de qu madera estn hechos, cundo se fabricaron, etc. pero nuestro nico mtodo de acceso GetKind es suficiente para introducir polimorfismo. El programa tiene la siguiente interfaz (figuras 1 y 2):

Figura 1: Ejemplo de interfaz para sustitucin con enlazado dinmico

Aprendiendo OOP con Delphi

Pgina 2 de 24

Creado el 17/06/09

Figura 2: Componentes de la interfaz de usuario

En ste ejemplo tambin introducimos una herramienta conveniente, llamada Class Completion (mencionada brevemente en el captulo 4), que acelera la creacin de nuestras propias clases As que ste ejemplo cubrir un montn de cosas interesantes!

Ejemplo 6.1 paso 1: Creando las Clases y TiposInicie una nueva aplicacin y aada una segunda unit a ella, a travs de la secuencia de men File | New | Unit. Almacene sta unit (llamada Unit2) como FurnitureU.pas y luego introduzca el programa como sigue. Comience slo con las declaraciones de clase (lneas 311 debajo). Luego coloque el cursor sobre la declaracin de la funcin GetKind ... (lnea 6) y presione (o haga click con el bot derecho y seleccione Complete Class at Cursor) para invocar a Class Completion. Esto crear un esqueleto para el mtodo. Ahora es simplemente cuestin de introducir el cdigo de programa (lnea 16).1 unit FurnitureU; 2 interface 3 type 4 TFurniture = class (TObject) 5 public 6 function GetKind: string; 7 end; // TFurniture = class (TObject) 8 9 10 11 TChair = class (TFurniture) end; // TChair = class (TFurniture) TTable = class (TFurniture) end; // TTable = class (TFurniture)

12 implementation 13 14 15 16 17 { TFurniture } function TFurniture.GetKind: string; begin Result := 'Furniture'; end; // function TFurniture.GetKind: string

18 end. // end Unit FurnitureU

Este es un extrao juego de clases! Tenemos tres clases: TFurniture, derivada desde TObject (lnea 4), tiene un mtodo, GetKind, que retorna un string. La segunda y tercera clases, TChair y TTable, estn derivadas desde TFurniture y no tienen ni datos ni mtodos en ste nivel. Expanderemos estas clases dentro de poco, pero por ahora necesitamos un conductor para testear stas definiciones de clase.

Aprendiendo OOP con Delphi

Pgina 3 de 24

Creado el 17/06/09

Ejemplo 6.1 paso 2: Probando las ClasesUsaremos el Form y Unit1 para escribir un conductor de programa (figuras 1 y 2). Para aadir los Items.Strings del RadioGroupBox, seleccione el RadioGroupBox y haga click sobre los tres puntos de la propiedad Items en el Inspector de Objetos. Aparecer el editor de propiedades. Introduzca Furniture, Chair, Table, cada uno en una nueva lnea. Advierta que aadimos un campo de datos pivado de tipo TFurniture a nuestra clase de interfaz de usuario (p.ej., para el formulario) en la lnea 16 debajo y por eso debemos aadir FurnitureU a la clusula uses (lnea 5).1 unit SubstitutionU; 2 interface 3 uses 4 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, 5 Dialogs, StdCtrls, ExtCtrls, FurnitureU; 6 type 7 TfrmSubstitution = class(TForm) 8 btnKind: TButton; 9 lblKind: TLabel; 10 btnFree: TButton; 11 rgbFurniture: TRadioGroup; 12 procedure btnKindClick(Sender: TObject); 13 procedure btnFreeClick(Sender: TObject); 14 procedure rgbFurnitureClick(Sender: TObject); 15 private 16 MyFurniture: TFurniture; // Declarando un objeto aplicacin 17 end; // TfrmPolymorphism = class(TForm) 18 var 19 frmSubstitution: TfrmSubstitution; 20 implementation 21 {$R *.DFM} 22 procedure TfrmSubstitution.rgbFurnitureClick(Sender: TObject); 23 begin 24 FreeAndNil (MyFurniture); // Limpiando las referencias 25 26 27 28 29 30 case 0: 1: 2: end; rgbFurniture.ItemIndex of MyFurniture := TFurniture.Create; MyFurniture := TChair.Create; // sustitucin MyFurniture := TTable.Create; // sustitucin

lblKind.Caption := '';

31 end; // fin procedure TfrmSubstitution.rgbFurnitureClick 32 procedure TfrmSubstitution.btnKindClick(Sender: TObject); 33 begin 34 if MyFurniture = nil then 35 lblKind.Caption := 'Object not defined' 36 else 37 lblKind.Caption := MyFurniture.GetKind; 38 end; // procedure TfrmSubstitution.btnTypeClick 39 procedure TfrmSubstitution.btnFreeClick(Sender: TObject);

Aprendiendo OOP con Delphi

Pgina 4 de 24

Creado el 17/06/09

40 begin 41 if MyFurniture = nil then 42 lblKind.Caption := 'No object exists' 43 else 44 begin 45 FreeAndNil (MyFurniture); 46 lblKind.Caption := 'Object freed' 47 end; 48 rgbFurniture.ItemIndex := -1; // Limpia indicacin 49 end; // fin procedure TfrmSubstitution.btnFreeClick 50 end. // unit SubstitutionU

Las lneas 27 y 28 son interesantes. MyFurniture est declarado como un TFurniture (lnea 16) y por tanto no nos debe sorprender que podamos asignarlo a una instancia de TFurniture en la lnea 26. Pero en las lneas 27 y 28 asignamos una referencia del tipo TFurniture a instancias de tipo TChair y TTable. Podemos hacer esto porque TChair y TTable estn derivados desde TFurniture y por tanto podemos tomar ventaja de la sustitucin. Ejecute el programa y testelo. Picando sobre cualquiera de los RadioButtons y luego el botn Kind displaya el mensaje Furniture. Veremos el mismo mensaje si MyFurniture es un TFurniture, un TChair o un TTable ya que la lnea 37 anterior invoca al mtodo GetKind definido en TFurniture (paso 1, lneas 1417) a travs de las relaciones de herencia entre las clases. Pero No sera mejor si la instancia de TChair pudiera declararse a s misma como una silla y la instancia mostrase que es una mesa? Lo haremos en el siguiente paso. Pero antes de hacerlo, advierta el cuidado puesto en ste programa a la hora de crear y liberar el objeto y comprobar su existencia. Ya que un objeto contina existiendo tras la terminacin del rgbFurnitureClick, los otros dos manejadores de evento tambin comienzan con la comprobacin de su existencia.TTable

Ejemplo 6.1 paso 3: Mtodos de Subclase Vinculados EstticamenteQueremos que las instancias TChair y TTable sean capaces de responder individualmente sobre el tipo de mueble que son, as que modificaremos las definiciones de clase para darles sus propios mtodos (recuerde usar Code Completion. Primero teclee slo las declaraciones de mtodo adicionales, en las lneas 910, 13 14 debajo. Coloque el cursor sobre las declaraciones, y presione . Delphi inserta los esqueletos de cdigo relevantes, dejando al programador insertar los estamentos de programa requeridos (lneas 25, 30)).1 unit FurnitureU; 2 interface 3 type 4 TFurniture = class (TObject) 5 public 6 function GetKind: string; 7 end; // TFurniture = class (TObject) 8 9 10 11 12 13 14 15 TChair = class (TFurniture) public function GetKind: string; end; // TChair = class (TFurniture) TTable = class (TFurniture) public function GetKind : string; end; // TTable = class (TFurniture)

16 implementation

Aprendiendo OOP con Delphi

Pgina 5 de 24

Creado el 17/06/09

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

{ TFurniture } function TFurniture.GetKind: string; begin Result := 'Furniture'; end; // function TFurniture.GetKind: string { TChair } function TChair.GetKind: string; begin Result := 'A Chair'; end; // end function Tchair.GetKind { TTable } function TTable.GetKind: string; begin Result := 'A Table'; end; // end function Ttable.GetKind

32 end. // end Unit FurnitureU

Ejecute y testee el programa. El resultado es un poco decepcionante! No importa si picamos sobreFurniture, Chair o Table porque siempre obtenemos el mismo mensaje Furniture desde el mtodo GetKind de TFurniture. As que aunque las subclases tienen sus propios mtodos, cuando ellos han sido

asignados al tipo de variable de la superclase, ellas invocan al mtodo de la superclase y no a los suyos propios. Echemos un vistazo de nuevo a esto en trminos de cdigo de programa. Cuando el compilador llega a la lnea 37 del paso 2, el compilador no tiene forma de saber si MyFurniture es de tipo TFurniture, TChair o TTable. La nica pista que tiene es que en el paso 2 lnea 16 declaramos la variable Furniture para ser de tipo TFurniture. As que cuando Delphi compila el paso 2 lnea 37, ste enlaza las llamadas a mtodo que hace la variable MyFurniture a la clase TFurniture, independientemente de a qu clase ha sido asignada MyFurniture, porque as es como la variable Furniture ha sido declarada. A esto se le llama enlazado en tiempo de compilacin, esttico o temprano entre la variable y el mtodo. Sin embargo, esto no es lo que queremos. Queremos que Delphi asocie los mtodos con la clase del objeto particular que estamos usando en cada momento de la ejecucin . Si estamos usando la superclase porque la ms reciente ejecucin del estamento Case caus la ejecucin del paso 2 lnea 26, queremos que Delphi llame al mtodo de la superclase cuando llega al paso 2 lnea 37. Sin embargo, si estamos usando una subclase, (p.ej., si estamos en el paso 2 lneas 27 28 ms recientemente ejecutado), queremos que Delphi sea capaz para llamar al mtodo de la subclase apropiada en el paso 2 lnea 37. Como el siguiente paso muestra, podemos asegurar esto usando enlazado en tiempo de ejecucin, dinmico o tardo (los tres trminos se refieren a la misma cosa).

Ejemplo 6.1 paso 4: Enlazado Dinmico y PolimorfismoQueremos que los mtodos de las subclases sean capaces de solapar los mtodos de la superclase cuando estemos trabajando con las subclases. Para hacer esto, la superclase debe tener mtodos virtuales (p.ej., debe usar enlazado dinmico). Los mtodos virtuales son solapados por mtodos de subclase siempre que la subclase est sustituyendo a la superclase. Hacer esto es sorprendentemente sencillo. Slo tenemos que cambiar las declaraciones de para indicar que el mtodo de la superclase es un mtodo virtual (p.ej., capaz de ser solapado) y que los mtodos de las subclases son mtodos solapados (lneas 6, 10 y 14 debajo). No necesitamos cambiar las implemententaciones de mtodo actuales.3 type 4 TFurniture = class (TObject) 5 public 6 function GetKind: string; virtual; 7 end; // TFurniture = class (TObject) 8 TChair = class (TFurniture)

Aprendiendo OOP con Delphi

Pgina 6 de 24

Creado el 17/06/09

9 public 10 function GetKind: string; override; 11 end; // TChair = class (TFurniture) 12 TTable = class (TFurniture) 13 public 14 function GetKind : string; override; 15 end; // TTable = class (TFurniture)

Ejecute el programa y experimente con l para ver qu hace. Ahora si hace click sobre cualquier y despus hace click sobre el botn Kind obtendr la respuesta que estamos esperando el mensaje displaya el tipo de objeto que fue creado cuando hizo click sobre el RadioButton-.RadioButton

Por ltimo, los mtodos de subclase estn siendo asociados con las instancias de subclases ya que son creadas en tiempo de ejecucin. As que el mismo estamento (paso 2 lnea 37) resulta en diferentes mtodos que son llamados, y por tanto de distinta forma, dependiendo del tipo de objeto que est actualmente asignado a esa variable. Esto es polimorfismo, y el paso 2 lnea 37 es una llamada polimrfica. Dos factores son necesarios para el polimorfismo. Primero, debe haber una sustitucin (jerrquica) de forma que una instancia de una subclase pueda ser asignada a una variable del tipo de la superclase. Segundo, debe haber enlazado dinmico de forma que el mtodo que es llamado sea determinado por la instancia en tiempo de ejecucin (como mencionamos arriba, el enlazado esttico se establece durante la compilacin. ste llama al mtodo determinado por el tipo de la declaracin de variable no afectada por consideraciones de tiempo de ejecucin). La sustitucin funciona slo en una direccin. Si nosotros declaramos la variable para ser de un tipo de subclase no podremos entonces sustituirla por una instancia de superclase. Esto tiene sentido: prcticamente, la superclase no toma el rol de subclase porque las subclases puede tener variables y/o mtodos adicionales no presentes en la superclase. Estas variables y/o mtodos quedaran indefinidos si una superclase fuera asignada a la subclase. Semnticamente tampoco tiene sentido para la superclase ser sustituta de una subclase. Mientras que TTable es un TFurniture, TFurniture no es necesariamente un TTable: TFurniture no puede actuar como un Tchair porque TChair es derivado desde TFurniture y no viceversa.

Ejemplo 6.1 paso 5: Diagramas UML de Clases y ObjetosLa declaracin de MyFurniture:TFurniture; como parte de TfrmSubstitution (paso 2 lnea 16) establece una asociacin entre esas dos clases. Podemos mostrar esto explcitamente como un enlace unidireccional en un diagrama de clases UML (Figura 3).

Aprendiendo OOP con Delphi

Pgina 7 de 24

Creado el 17/06/09

Figura 3: Asociacin entre TfrmSubstitution y TFurniture

Sin embargo, debido a la sustitucin y al enlazado dinmico, un objeto TChair o un objeto TTable pueden tomar el rol de un objeto TFurniture. As las posibles asociaciones estn entre frmSubstitution (el objeto de interfaz de usuario) y una instancia de cualquier de los TFurniture, TChair o TTable. Si, por ejemplo, seleccionamos Chair sobre la interfaz de usuario (y as ejecutamos la lnea 27 del paso 2), obtenemos el siguiente diagrama de objeto (Figura 4):

Figura 4: Asociacin entre instancias

Ejemplo 6.1. SumarioEl polimorfismo es la combinacin de sustitucin y enlazado dinmico. Estableciendo una simple asociacin entre dos clases (p.ej., entre TfrmSubstitution y TFurniture), el polimorfismo nos ha permitido usar tres diferentes asociaciones (p.ej., entre TfrmSubstitution y TFurniture y sus dos subclases) sin la necesidad de especificar cada asociacin por separado individualmente. Esto reduce significativamente el acoplamiento entre objetos, una caracterstica importante en cualquier buen diseo de programa.

Ejemplo 6.2. Alternativas al PolimorfismoLos recin llegados a la OO a veces se sienten reacios a explotar el polimorfismo; lo ven demasiado extrao. Tan importante es? Cules son las alternativas? En ste caso, porque es un ejemplo mucho ms simple, la alternativa ms sencilla es no subclasearlo todo. En lugar de eso declararemos un atributo en la clase TFurniture para indicar Furniture, Chair o Table. Pero en los casos en los que las diferencias entre subclases son ms extensivas, combinarlo todo en una nica clase nos lleva a una clase que es muy grande y difcil de manejar, y hace los futuros cambios y el mantenimiento muy difciles. Entonces, Podemos retener la jerarqua de tres clases de dominio separadas del captulo anterior pero encontrar un programa alternativo que no use polimorfismo? El polimorfismo es la combinacin de la sustitucin y el enlazado dinmico. En el paso 1 de ste ejemplo vimos qu pasaba si no usbamos ni la sustitucin ni el enlazado dinmico. En el paso 2 usamos la sustitucin con el enlazado esttico.

Aprendiendo OOP con Delphi

Pgina 8 de 24

Creado el 17/06/09

Ejemplo 6.2 paso 1: Sin SustitucinSin sustitucin necesitamos declarar tres variables de referencia en la clase TfrmSubstitution, para FreeAndNil y tres referencias en los manejadores de evento rgbFurnitureClick y btnFreeClick, y luego para probar la existencia de los tres objetos en el manejador de eventos btnKindClick. Si experimenta escribiendo ste programa, ver que es muy desordenado, tal como el diagrama de clases sugiere (Figura 5, y comprela con la Figura 3). Los enlaces de asociacin entre clases se vuelven mucho ms complejos sin la sustitucin. Esto, a su vez, conduce a programas ms complejos y estrechamente unidos que se vuelven ms difciles de mantener.

Figura 5: Asociacin sin sustitucin

En la escritura de ste programa sin sustitucin, el nico beneficio que obtenemos desde la jerarqua de herencia es que TFurniture define una signatura, p.ej., el mtodo GetKind, con el cual son compatibles TChair y TTable.

Ejemplo 6.2 - paso 2: Sustitucin sin PolimorfismoConsiderando que el paso anterior no fue muy exitoso, Podemos solucionar el problema usando sustitucin pero sin enlazado dinmico? Para hacer esto, elimine el enlazado dinmico en la unit FurnitureU (p.ej., revierta desde el cdigo en el ejemplo 6.1 paso 4 al cdigo en el paso 3 comentando o eliminando las palabras clave virtual y override). Ahora reemplace el mensaje polimrfico (ejemplo 6.1, paso 2, lnea 37) con un complejo estamento If para testear la clase de la actual instancia MyFurniture y entonces llamar al mtodo GetKind requerido (lneas 3440 debajo).32 procedure TfrmSubstitution.btnKindClick(Sender: TObject); 33 begin 34 if MyFurniture = nil then 35 lblKind.Caption := 'Object not defined' 36 else if (MyFurniture is TChair) then 37 lblKind.Caption := (MyFurniture as TChair).GetKind 38 else if (MyFurniture is TTable) then 39 lblKind.Caption := (MyFurniture as TTable).GetKind 40 else 41 lblKind.Caption := MyFurniture.GetKind; 42 end; // procedure TfrmSubstitution.btnTypeClick(Sender: TObject)

Lo que antes era una simple lnea con el polimorfismo (paso 2, lnea 37) ahora requiere un complejo juego de estamentos if con enlazado esttico. Debemos probar esto explcitamente para cada posible tipo de instancia (usando informacin RTTI a travs del operador 1 is tal como se discuti en el captulo 5) y luego convertirlo especficamente (usando el operador as) para invocar el mtodo correcto.1 RTTI no es parte de la especificacin .NET, paro Delphi 8 para .NET todava tiene disponibles el is, as y operadores similares

Aprendiendo OOP con Delphi

Pgina 9 de 24

Creado el 17/06/09

A diferencia de la situacin del ejemplo 6.1, paso 3, stas instancias ahora enlazan a los mtodos requeridos. En tiempo de compilacin, el compilador sabe que MyFurniture en la lnea 37 debe ser tratado como un TChair debido al operador de conversin as. As que en la lnea 37, el objeto MyFurniture est estticamente enlazado al mtodo GetKind del TChair. Similarmente en la lnea 39, el compilador tiene suficiente informacin en tiempo de compilacin para enlazar MyFurniture al mtodo GetKind del TTable. Sin ninguna conversin en la lnea 41, MyFurniture est estticamente enlazado al mtodo GetKind del TFurniture porque MyFurniture est declarado como TFurniture. Para testear esto, elimine los typecasts en las lneas 37 y 39. Obtendr el mtodo de TFurniture con las instancias de TChair y TTable. Alternativamente, si hace las conversiones incorrectas en las lneas 37 y 39 (y usa la antigua conversin estilo Pascal la cual no hace control de errores), el enlazado esttico enlazar con los mtodos incorrectos, lanzando el mensaje incorrecto:36 else if (MyFurniture 37 lblKind.Caption := 38 else if (MyFurniture 39 lblKind.Caption := is TChair) then TTable(MyFurniture).GetKind // wrong typecast is TTable) then TChair(MyFurniture).GetKind // wrong typecast

Debido a que todava tenemos sustitucin, el orden de evaluacin en los estamentos condicionales es importante. Si testeamos primero la clase ms alta en la jerarqua, en ste caso TFurniture, todas las subclases tambin evaluarn a true porque cualquier subclase puede sustituir a su superclase. El enlazado esttico es ligeramente ms rpido que el enlazado dinmico, por lo que a veces se sugiere su uso para operaciones donde la velocidad es crtica. Pero todas las comparaciones IfElseIf introducen su propia penalizacin en el rendimiento, contrarrestando al rpido enlazado esttico, e introduciendo un considerable margen de error. En resumen, el enlazado esttico introduce una gran cantidad de complejidad de codificacin, hace el programa ms propenso a errores, hace ms difcil una posterior mejora del mismo, y en general, no introduce una significante mejora en la velocidad. Por tanto es recomendable el esfuerzo en comprender todas las capacidades del polimorfismo y pensar en las estructuras de herencia de una aplicacin cuidadosamente para maximizar la generalizacin y las posibilidades de la sustitucin.

Sumario de Enlazado Temprano y TardoDebido a la sustitucin, el programador puede asignar MyFurniture a cualquiera de los tres tipos, o TTable. Por tanto para una correcta operacin, Delphi debe decidir cul de los tres mtodos usar mientras el programa est funcionando, no antes. Esto requiere enlazado tardo (tambin llamado enlazado en tiempo de ejecucin o enlazado dinmico. Enlazado se refiere al enlace establecido entre el objeto y el mtodo). Delphi usa enlazado tardo cuando las palabras clave virtual y override son utilizadas en la declaracin de clases.TFurniture, TChair

Por defecto, Delphi usa enlazado temprano (tambin llamado enlazado en tiempo de compilacin o enlazado esttico). Esto establece el enlace entre un objeto y un mtodo mientras el programa est siendo compilado, antes de que ste se ejecute. Esto significa que Delphi no puede invocar diferentes mtodos dependiendo del tipo de objeto involucrado en tiempo de ejecucin y por tanto el enlazado temprano es no polimrfico.

Ejemplo 6.3. Evolucin en un Programa PolimrficoPara soportar la futura evolucin de un programa, el acoplamiento dentro del programa debera mantenerse bajo, particularmente en aquellas partes de un programa que estn sujetas a cambio. Esta es una importante razn para usar polimorfismo, y la reduccin del acoplamiento a travs del polimorfismo es una importante consideracin en muchos patrones.

Aprendiendo OOP con Delphi

Pgina 10 de 24

Creado el 17/06/09

Este ejemplo ilustra cmo el acoplamiento reducido en un programa polimrfico facilita una futura evolucin. Tambin examina la combinacin de la herencia para reutilizacin para el comportamiento polimrfico. Supongamos que necesitamos introducir otro nivel de especializacin en ste programa mediante el subclaseado de TTable en TCoffeeTable y TKitchenTable (Figura 6). Qu cambios debemos realizar?

Figura 6: Introduciendo dos subclases ms

Ejemplo 6.3 paso 1: Las Subclases AdicionalesNuestro primer paso es definir las subclases adicionales en la Figura 7.

Figura 7: Un nivel (polimorfico) adicional

Comience con la versin polimrfica del programa (ejemplo 6.1, paso 4) y aada las dos nuevas subclases (lneas 1623, 4049 debajo, y recuerde el uso de Class Completion ). Para mostrar la combinacin de la herencia para reutilizacin (subclaseado) con herencia para polimorfismo (subtipado), tambin hemos cambiado las definiciones de mtodos existentes para invocar el mtodo inmediatamente ancestro a travs de la palabra clave inherited (lneas 33, 38).1 unit FurnitureU; 2 interface 3 type

Aprendiendo OOP con Delphi

Pgina 11 de 24

Creado el 17/06/09

4 12 13 14 15 16 17 18 19 20 21 22 23

{TFurniture and TChair definitions as before} TTable = class (TFurniture) // unchanged public function GetKind: string; override; end; // TTable = class (TFurniture) TCoffeeTable = class (TTable) // new public function GetKind : string; override; end; // TCoffeeTable = class (Ttable) TKitchenTable = class (TTable) // new public function GetKind : string; override; end; // TKitchenTable = class (Ttable)

24 implementation 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 { TFurniture } function TFurniture.GetKind: string; begin Result := 'Furniture'; end; // function TFurniture.GetKind: string { TChair } function TChair.GetKind: string; begin Result := 'A Chair, ' + inherited GetKind; end; // end function Tchair.GetKind { TTable } function TTable.GetKind: string; begin Result := 'A Table, ' + inherited GetKind; end; // end function Ttable.GetKind { TCoffeeTable } function TCoffeeTable.GetKind: string; begin Result := 'Coffee Table, ' + inherited GetKind; end; // end function TcoffeeTable.GetKind { TKitchenTable } function TKitchenTable.GetKind: string; begin Result := 'Kitchen Table, ' + inherited GetKind; end; // end function TkitchenTable.GetKind

50 end. // end Unit FurnitureU

Varias cosas distintas estn ocurriendo aqu, as que las veremos una por una. Primero derivamos las dos nuevas subclases, TCoffeeTable y TKitchenTable desde TTable (lneas 1623), por tanto aadiendo otra capa a la jerarqua (Figura 7). Cada una de ellas tiene su propio mtodo GetKind declarado como override, lo cual por tanto sobreescribe el mtodo GetKind que heredaran por defecto de TTable. Estas subclases pueden usar cualquiera de los mtodos que heredan desde lo alto de la jerarqua, y pueden sobreescribir cualquiera de los mtodos dinmicamente (p.ej., usando enlazado tardo para permitir polimorfismo) usando la palabra clave override teniendo en cuenta que el mtodo sobreescrito es tanto un mtodo virtual como es por s mismo un mtodo sobreescrito (p.ej., lnea 14). Por tanto GetKind en la lnea 18 sobreescribe a su mtodo ancestro el cual de hecho sobreescribe a su mtodo ancestro (lnea 14).

Aprendiendo OOP con Delphi

Pgina 12 de 24

Creado el 17/06/09

Podemos sobreescribir mtodos existentes a cualquier nivel que necesitemos. Pero Qu pasa si actualmente queremos usar un mtodo ancestro aunque pensemos que ha sido sobreescrito? Esto resulta ser muy sencillo: Delphi tiene la palabra clave inherited que nos permite acceder al mtodo ancestro. Esto es til cuando no queremos reemplazar el mtodo ancestral, pero necesitamos extenderlo para el descendiente. En ste caso, sobreescribimos el mtodo ancestro, lo heredamos para obtener su funcionalidad y entonces aadimos cualquier cdigo que necesitemos para extenderlo. En resumen, reemplazamos un mtodo ancestral redeclarando el mtodo en la subclase con exactamente el mismo nombre y lista de parmetros que tiene en la superclase. Si queremos que esto sea una sustitucin polimrfica usando enlazado tardo, la declaracin raz del mtodo debe ser virtual con una declaracin override por cada vez que es redeclarado a lo largo de la jerarqua. Donde queramos extender el mtodo ancestral, lo invocaremos en el mtodo de la subclase a travs de la palabra clave inherited. Esta es la teora, en cualquier caso. No lo hemos visto an en accin. Por tanto modificaremos el programa conductor para incorporar estas clases adicionales.

Ejemplo 6.3 paso 2: Llamando a Subclases AdicionalesKitchenTable a rgbFurniture.

Extienda la interfaz de usuario como se muestra en la Figura 6 aadiendo los elementos CoffeeTable y Extienda el manejador de evento rgbFurnitureClick como sigue:22 procedure TfrmSubstitution.rgbFurnitureClick(Sender: TObject); 23 begin 24 FreeAndNil (MyFurniture); // Clean up the references 25 26 27 28 29 30 31 case 0: 1: 2: 3: 4: end; rgbFurniture.ItemIndex of MyFurniture := TFurniture.Create; MyFurniture := TChair.Create; // 4 substitutions MyFurniture := TTable.Create; MyFurniture := TCoffeeTable.Create; MyFurniture := TKitchenTable.Create;

32 lblKind.Caption := ''; 33 end; // end procedure TfrmSubstitution.rgbFurnitureClick 34 procedure TfrmPolymorphism.btnKindClick(Sender: TObject); 35 begin 36 if MyFurniture = nil then 37 lblKind.Caption := 'Object not defined' 38 else 39 lblKind.Caption := MyFurniture.GetKind; // runtime binding 40 end; // procedure TfrmPolymorphism.btnTypeClick(Sender: TObject)

El nico cambio en la programacin que tenemos que hacer en la clase cliente es instanciar y asignar estas dos nuevas subclases (lneas 2930). Debido al enlazado tardo (en tiempo de ejecucin), no hacemos ningn cambio en el estamento que usa estas instancias (lneas 3440 estn inalteradas desde las lneas 32 38 en el ejemplo 6.1, paso 2). Este es el poder del polimorfismo. Cada subclase sabe cmo hacer lo que tiene que hacer como resultado de s misma y sus mtodos heredados. Si estos mtodos permiten enlazado en tiempo de ejecucin, el programa sabe a qu mtodo llamar dependiendo de la identidad del objeto. Esto significa que si aadimos subclases adicionales a un programa existente no tenemos que ir a travs del programa aadiendo llamadas a estas nuevas subclases. Gracias al polimorfismo los mtodos correctos sern llamados automticamente. En esencia, el programa slo necesita decirle a un objeto que haga X,Y y Z. Cada clase distinta de la jerarqua sabe cmo debe hacer XYZ y por tanto va y lo hace. El programa que usa el objeto no tiene por qu saber cmo el objeto realiza la operacin requerida, slo que l va a hacer lo que sea apropiado.

Aprendiendo OOP con Delphi

Pgina 13 de 24

Creado el 17/06/09

Para ver esto en accin, ejecute el programa. Seleccione un RadioButton y luego haga click sobre el botn Kind. Un mensaje identificando al objeto seleccionado y su ancestro aparecer. Aunque slo hay una instruccin que cause la presentacin en pantalla (lnea 40 arriba), cada objeto sabe quin es y quin es su ancestro y por tanto crea un display diferente a pesar de recibir idntico mensaje. El polimorfismo simplifica el proceso de modificar y evolucionar un programa y minimiza sobre el cliente el impacto de aadir o eliminar clases (la llamada polimrfica en la lnea 43 anterior es completamente inmune a la adicin o sustraccin de descendientes de TFurniture ya que slo se basa en la signatura del mtodo GetKind). La sustitucin tambin reduce el acoplamiento entre objetos ya que un enlace de asociacin es vlido para las llamadas designadas y todos sus descendientes (el campo privado en TfrmSubstitution que almacena una referencia a to TFurniture (ejemplo 6.1, paso 2, lnea 16) activa una instancia de TfrmSubstitution para ser asociada con una instancia de cualquiera de las cinco clases en la jerarqua TFurniture).

Ejemplo 6.4. Mtodos AbstractosPor tanto, todo esto es muy bonito y est muy pulido, pero no hemos escogido nuestro ejemplo con mucho cuidado? S, lo hemos hecho, en orden a concentrarnos en los principios esenciales. Sin embargo, existen otras circunstancias que tambin deberamos poder acomodar. Por ejemplo, los mtodos sobreescritos son todos similares en el ejemplo anterior. Qu pasa si ellos fueran muy distintos? Digamos que nuestra clase base se llama MyShape. Como subclases tenemos MyEllipse y MyRectangle. Queremos crear un mtodo Draw. MyShape.Draw no tiene sentido alguno -Qu va a dibujar, un rectngulo o una elipse?- Por tanto, aunque cada subclase MyShape que hemos creado necesita un mtodo Draw, no podemos definir Draw para la clase base de la forma usual. En una situacin como sta podemos usar un mtodo abstracto. Este es un mtodo que declaramos como abstracto en la superclase. Ser abstracto significa que no tiene una implementacin y por tanto no existe en la superclase. Cada subclase entonces proporciona su propia implementacin. Tal vez sea ms sencillo de entender si trabajamos con un ejemplo. Crearemos una clase TmyShape con un mtodo abstracto Draw y dos subtipos, TMyEllipse y TMyRectangle. Los subtipos tendrn mtodos Draw concretos para dibujar una elipse o un rectngulo (Figura 8) y por tanto sobreescriben la declaracin abstracta en la superclase. Sobreescribir implica enlazado dinmico, y por tanto la declaracin abstract tambin debe ser hecha virtual.

Figura 8: Ilustrando un mtodo virtual abstracto

Aprendiendo OOP con Delphi

Pgina 14 de 24

Creado el 17/06/09

Ejemplo 6.4 paso 1: Creando un Mtodo AbstractoInicie una nueva aplicacin. Aada una segunda unit para las definiciones de clases como se muestra a continuacin.1 unit MyShapesU; 2 interface 3 uses ExtCtrls; 4 type 5 TMyShape = class (TObject) 6 public 7 procedure Draw (AnImage: TImage; ABorder: integer); virtual; abstract; 8 9 end; // end TMyShape = class (TObject) 10 TMyEllipse = class (TMyShape) 11 public 12 procedure Draw (AnImage: TImage; AnBorder: integer); override; 13 end; // end TMyEllipse = class (TmyShape) 14 TMyRectangle = class (TMyShape) 15 public 16 procedure Draw (AnImage: TImage; ABorder: integer); override; 17 end; // end TMyRectangle = class (TmyShape) 18 implementation 19 20 21 22 23 24 25 26 27 28 29 30 31 32 { TMyEllipse } procedure TMyEllipse.Draw(AnImage: TImage; ABorder: integer); begin AnImage.Canvas.Rectangle(0, 0, AnImage.Width, AnImage.Height); AnImage.Canvas.Ellipse (ABorder, ABorder, AnImage.Width - ABorder, AnImage.Height - ABorder); end; // end procedure TmyEllipse.Draw { TMyRectangle } procedure TMyRectangle.Draw(AnImage: TImage; ABorder: integer); begin AnImage.Canvas.Rectangle(0, 0, AnImage.Width, AnImage.Height); AnImage.Canvas.Rectangle (ABorder, ABorder, AnImage.Width - ABorder, AnImage.Height - ABorder); end; // end procedure TmyRectangle.Draw

33 end. // end MyShapesU

Como se indica en la Figura 10, tenemos una clase base, TMyShape (lneas 59), derivada desde TObject y dos subclases, TMyEllipse y TMyRectangle, derivadas desde TMyShape (lneas 1017). Cada una de ellas declara un mtodo Draw con su procedimiento. Para TMyShape, el mtodo Draw es declarado como virtual (lneas 78), por tanto puede ser sobreescrito dinmicamente por una subclase, y como abstract, lo que significa que no tiene una implementacin. Para mostrar que es abstracto, el mtodo Draw de TMyShape est en itlica en la Figura 10. Mirando la signatura de ste mtodo dado por la declaracin, vemos que el mtodo Draw tiene dos parmetros, un TImage donde el objeto debe ser dibujado, y un valor integer ABorder para especificar a qu distancia del centro del TImage debe ser dibujado. TImage es parte de la VLC de Delphi y displaya una imagen grfica en un formulario. Para ms informacin, consulte la ayuda en lnea de Delphi.

Aprendiendo OOP con Delphi

Pgina 15 de 24

Creado el 17/06/09

Figura 10: Un mtodo abstracto en la clase base

Una implementacin para Draw debe ser proporcionada en cada rama derivada de TMyShape. As, en la seccin de implementacin (lneas 1833), hay una implementacin para TMyEllipse.Draw (lneas 2025) y para TMyRectangle.Draw (lneas 2732). Como TMyShape.Draw es abstracto no tiene implementacin. Los mtodos como TMyEllipse.Draw o TMyRectangle.Draw, el cual implementa un mtodo abstracto, son frecuentemente llamados mtodos concretos. Aqu, TMyShape establece un tipo (slo el mtodo Draw en ste ejemplo) el cual est implementado en los subtipos TMyEllipse y TmyRectangle.

Ejemplo 6.4 paso 2: Testeando la Clase MyShapeEn la interfaz de usuario (figuras 8 y 9) coloque dos ComboBox, un Button y un Image (desde la pestaa adicional).1 unit PolyShapeU; 2 interface 3 uses 4 Windows, Messages, SysUtils, Variants, Classes, Graphics, 5 Controls, Forms, Dialogs, StdCtrls, ExtCtrls, MyShapesU; 6 type 7 TfrmPolyShape = class(TForm) 8 { standard RAD declarations } 15 private 16 MyEllipse: TMyEllipse; 17 MyRectangle: TMyRectangle; 18 end; // TfrmPolyShape = class(TForm) 19 20 21 var frmPolyShape: TfrmPolyShape; implementation

22 {$R *.dfm} 23 procedure TfrmPolyShape.FormCreate(Sender: TObject); 24 begin 25 MyEllipse := TMyEllipse.Create; 26 MyRectangle := TMyRectangle.Create; 27 end; // end procedure TfrmPolyShape.FormCreate 28 procedure TfrmPolyShape.btnDrawClick(Sender: TObject); 29 var 30 Border: integer; 31 MyShape: TMyShape; // polymorphic reference

Aprendiendo OOP con Delphi

Pgina 16 de 24

Creado el 17/06/09

32 begin 33 case cboSize.ItemIndex of 34 0: Border := 10; 35 else Border := 50; 36 end; // end case cboSize.ItemIndex 37 38 39 40 case cboShape.ItemIndex of 0: MyShape := MyEllipse; // substitution else MyShape := MyRectangle; // substitution end; // end case cboShape.ItemIndex

41 MyShape.Draw (imgDrawShape, Border); // polymorphic message 42 end; // end procedure TfrmPolyShape.btnDrawClick 43 procedure TfrmPolyShape.FormDestroy(Sender: TObject); 44 begin 45 MyEllipse.Free; 46 MyRectangle.Free; 47 inherited; 48 end; // end procedure TfrmPolyShape.FormDestroy 49 end. // end unit PolyShapeU

Aqu cambiamos nuestra aproximacin previa ligeramente para enfatizar ligeramente distintos aspectos. Declaramos dos campos de datos privados para frmPolyShape, uno para cada tipo concreto que nosotros definimos en MyShapeU (lneas 1517) y los instanciamos cuando creamos el form (lneas 2627). Luego, en btnDrawClick, asignamos uno u otro de estos MyShape (lneas 3940), una referencia localmente declarada a TMyShape. Debido a la sustitucin, podemos asignar una instancia de una clase hija a una variable de la clase padre. Por tanto, dependiendo de si la lnea 38 o la 39 es ejecutada, MyShape se refiere tanto a MyEllipse o a MyRectangle. En la lnea 41, hacemos una llamada polimrfica usando la referencia MyShape y as dibujamos tanto una elipse como un recuadro en base a la asignacin en la sentencia Case (lneas 3841). Esto es as debido a que los mtodos GetKind son declarados como mtodos virtual y override y por tanto utilizan enlazado tardo. El estamento MyShape.Draw llamara al mtodo de TmyShape GetKind slo si MyShape es instanciado como un TMyShape. Intentar esto nos llevara a un error porque el mtodo de TmyShape GetKind es abstracto (ejemplo 6.4, paso 1, lneas 78) y no tiene una implementacin concreta. Los objetos MyEllipse y MyRectangle persisten durante la vida del form desde que los creamos en el manejador de evento del form OnCreate y los liberamos en el manejador de eventos OnDestroy. Sin embargo, nosotros nunca usamos esto directamente. En su lugar, asignamos uno u otro a MyShape y lo usamos. Esto significa que no tenemos que crear y liberar instancias como hicimos en los ejemplos anteriores de ste captulo. Nunca instanciamos MyShape directamente, sino que lo usamos para almacenar una referencia a uno de los objetos que tenemos creados. Estrictamente hablando, no necesitamos liberar los objetos en el manejador de eventos OnDestroy del formulario, ya que Delphi automticamente libera toda la memoria que almacena cuando el programa termina. Sin embargo, esto no hace dao a la liberacin de estos objetos. Aqu se hace hincapi en la secuencia crear-usar-liberar en el uso de objetos que discutimos en los captulos anteriores y el concepto de que si un objeto crea cualesquiera otros objetos, ste generalmente tiene la responsabilidad de liberarlos antes de que l mismo sea destruido.

Aprendiendo OOP con Delphi

Pgina 17 de 24

Creado el 17/06/09

Ejemplo 6.4 paso 3: Un Mtodo de Ayuda en la SuperclaseVolviendo a la definicin de TMyShape y sus descendientes vemos que ambos mtodos GetKind tienen el mismo estamento para limpiar el componente Image y para colocar un borde alrededor (ejemplo 6.4, paso 1, lneas 22 y 29). Aunque sta repeticin no es crucial en un pequeo ejemplo como ste, en un principio intentamos evitar duplicidad colocando cdigo repetido en un mtodo separado, a veces llamado un mtodo de ayuda porque es slo para uso dentro de la clase. Si declaramos ste mtodo como parte de la clase base, como hacemos en las lneas 910 debajo, cada subclase puede acceder a l a travs de la herencia mejor que definirla separadamente un agradable trozo de reutilizacin-. Qu tipo de visibilidad debera debera asignarse a un mtodo de ayuda como ste? No queremos hacerlo pblico ya que otras units podran entonces usarla tambin, comprometiendo la encapsulacin. Aqu definimos TMyShape, TMyEllipse y TMyRectangle en la misma unit. Los especificadores de visibilidad de Delphi se aplican a una unit2, y por tanto podemos especificar los mtodos de ayuda del padre como privados: las subclases todava pueden acceder a ellos. Si codificamos las subclases en units separadas, podramos hacer el mtodo de ayuda protected. Esto le hace accesible a las clases descendientes del padre, aunque si ellos estn en una unit diferente, pero no para nadie ms. Aqu declaramos el mtodo de ayuda como protected en la superclase (lneas 910) y lo invocamos desde la subclase (lneas 24, 31).1 unit MyShapesU; 2 interface 3 uses ExtCtrls; 4 type 5 TMyShape = class (TObject) 6 public 7 procedure Draw (AnImage: TImage; ABorder: integer); virtual; abstract; 8 9 protected 10 procedure Clear (AnImage: TImage); // concrete: subclasses use it 11 end; // end TMyShape = class (TObject) 12 TMyEllipse = class (TMyShape) 13 public 14 procedure Draw (AnImage: TImage; ABorder: integer); override; 15 end; // end TMyEllipse = class (TmyShape) 16 TMyRectangle = class (TMyShape) 17 public 18 procedure Draw (AnImage: TImage; ABorder: integer); override; 19 end; // end TMyRectangle = class (TmyShape) 20 implementation 21 22 23 24 25 26 272

{ TMyEllipse } procedure TMyEllipse.Draw(AnImage: TImage; ABorder: integer); begin Clear (AnImage); AnImage.Canvas.Ellipse (ABorder, ABorder, AnImage.Width - ABorder, AnImage.Height - ABorder); end; // end procedure TmyEllipse.Draw

Delphi 8 introduce especificadores adicionales strictly private y strictly protected los cuales se refieren a la estructura de clase slo, sin concernir a las units donde estn declaradas.

Aprendiendo OOP con Delphi

Pgina 18 de 24

Creado el 17/06/09

28 29 30 31 32 33 34 35 36 37 38 39

{ TMyRectangle } procedure TMyRectangle.Draw(AnImage: TImage; ABorder: integer); begin Clear (AnImage); AnImage.Canvas.Rectangle (ABorder, ABorder, AnImage.Width - ABorder, AnImage.Height - ABorder); end; // end procedure TmyRectangle.Draw { TMyShape } procedure TMyShape.Clear(AnImage: TImage); begin AnImage.Canvas.Rectangle (0, 0, AnImage.Width, AnImage.Height); end; // end procedure TmyShape.Clear

40 end. // end MyShapesU

Asignando ReferenciasEn el ejemplo 6.4, paso 2, declaramos una referencia a objeto ( MyShape, lnea 32) pero no lo instanciamos. En su lugar, lo asignamos a uno de los otros dos objetos que creamos (lnea 39 40). Vale la pena ver esto brevemente. Primero nos fijamos en la capa de memoria antes de que se ejecute el estamento Case (Figura 11). Tras el estamento Case, y asumiendo que el usuario seleccion el RadioButton Ellipse, MyShape se refiere a la misma instancia que MyEllipse (Figura 12).

Figura 11: Las referencias tras el funcionamiento del manejador de evento OnCreate del form

Aprendiendo OOP con Delphi

Pgina 19 de 24

Creado el 17/06/09

Figura 12: Tras la asignacin de una referencia a MyShape a una instancia a MyEllipse

Advierta dos cuestiones. Primero, podemos asignar ms de una referencia al mismo objeto. En la Figura 12, tanto MyEllipse como MyShape se refieren al mismo objeto. Declaramos referencias a TMyEllipse, TMyRectangle y TMyShape, pero slo creamos un TMyEllipse y un TMyRectangle. En las lneas 39 40 asignamos entonces una referencia adicional, MyShape, a uno de estos objetos. A menudo es importante poder asignar ms de una referencia a un objeto, y sta es una de las razones por las que Delphi (y Java) usan la semntica de la referencia. El segundo punto a notar es el principio de sustitucin: una variable que puede almacenar una referencia a un objeto de clase A puede tambin almacenar una referencia a un objeto de cualquier subclase de A.

Patrn 6.1. PolimorfismoA veces, se necesita un comportamiento alternativo sobre la base de un tipo del objeto. Una aproximacin a esto es usar una serie de estamentos If que testean el tipo de los objetos y entonces invocan al comportamiento requerido. Sin embargo hay varios problemas asociados a estos juegos de estamentos If. Ellos pueden rpidamente volverse complejos, haciendo difcil el testearlos y modificarlos, particularmente si similares evaluaciones If son necesarias a diferentes niveles de un programa. Por tanto, cuando el comportamiento requerido es determinado por el tipo de un objeto, considere usar una llamada polimrfica en lugar de usar un estamento If. El programador entonces no necesita codificar la lgica para determinar el tipo ya que el comportamiento resultante es producto de tanto la llamada del mtodo y el objeto receptor. Futuros cambios, tales como clases adicionales del mismo subtipo, son acomodados por los mtodos polimrficos de las nuevas clases en vez de ampliar los estamentos If. As los tipos que varan tienen la responsabilidad del comportamiento correcto. Esto reduce la posibilidad de introducir errores cuando los estamentos If han sido cambiados incorrectamente o pasados por alto. El polimorfismo por tanto puede hacer el cdigo ms simple de implementar y ms fiable. Dado que el polimorfismo se basa en la sustitucin, y por tanto en la generalizacin de jerarquas en el programa, el diseo del polimorfismo puede mejorar la estructura de herencia de la clase y reducir el acoplamiento (un simple enlace de asociacin puede ser usado para navegar entre todos los descendientes de las dos clases asociadas). Los inconvenientes de polimorfismo son que puede hacer el cdigo ms difcil de comprender y puede introducir clases adicionales en la estructura del programa. El polimorfismo es una diferencia crucial entre la programacin orientada a objetos y otros estilos de programacin. Usado con cuidado, el polimorfismo produce sistemas que son ms simples y tambin ms flexibles, y por tanto pueden ser cambiados ms fcilmente en funcin de los requerimientos. ste juega un importante rol en muchos patrones, y lo veremos con regularidad en los siguientes captulos.

Aprendiendo OOP con Delphi

Pgina 20 de 24

Creado el 17/06/09

Diferencias entre Subclaseado y SubtipadoEste captulo se refiere a la diferencia entre el subclaseado y el subtipado. Aunque sta distincin es crucial para usar polimorfismo efectivamente, ahora todos los autores lo mantienen. Estos trminos tambin a veces significan distintas cosas para diferentes personas. Seguimos las definiciones de Gang of Four (GoF) (Gamma et al, 1995, p17): La herencia de clases define una implementacin de objetos en trminos de la implementacin de otros objetos. As la herencia de clases (o subclaseado) permite la reutilizacin de campos de datos y mtodos, y es un mecanismo para compartir cdigo y representacin. Es por tanto una consideracin que aparece durante la implementacin y es la forma de herencia que usamos en los primeros cinco captulos de estas notas. En contraste, la interfaz de herencia (o subtipado) describe cundo un objeto puede ser usado en lugar de otro. El subtipado se preocupa por sustituir un padre por su hijo, y consecuentemente sobre el polimorfismo. Es un diseo muy considerable, aunque su implementacin necesita enlazado dinmico. El subtipado permite a una determinada interfaz ser definida en una clase padre. Las varias clases hijo entonces implementan sta interfaz de forma que cualquier hijo puede tomar el rol nominalmente asignado al padre. Los programas cliente declaran variables del tipo del padre slamente, a fin de que puedan permanecer desconocedores de la clase hija especfica la cual es enlazada dinmicamente en un momento determinado al tipo padre. Una importante distincin aqu es la diferencia entre enlazado esttico y dinmico. Con enlazado esttico, el iniciador del mensaje determina cmo el mensaje ser transportado a travs del direccionamiento de una clase especfica (la clase declarada) independientemente de la actual clase del receptor. Con enlazado dinmico, el iniciador del mensaje direcciona un tipo especfico en tiempo de ejecucin y el receptor realiza su (propia) implementacin del mensaje. Lo que hace un poco difcil de comprender la diferencia entre el subclaseado y el subtipado a primera vista es que ambos dependen de la herencia. Por lo tanto, es difcil de ver slo desde un vistazo rpido a un conjunto de definiciones de clase donde el subclaseado, subtipado o ambos estn siendo utilizados. Una pista til es dada por las palabras clave virtual, abstract y override, las cuales usualmente indican que el subtipado est siendo utilizado de una forma u otra. A medida que vayamos trabajando a travs de stas notas, la distincin se volver ms clara.

Captulo 6. SumarioPuntos principales: 1. Sustitucin con clases de programador 2. Enlazado temprano (esttico, en tiempo de compilacin) y tardo (dinmico, en tiempo de ejecucin) 3. Mtodos virtual y override 4. polimorfismo = sustitucin + enlazado dinmico 5. mltiples asociaciones a travs de un mismo enlace, acoplamiento reducido, evolucin 6. Mtodos abstractos 7. El concepto de tipo 8. El patrn polimrfico

Aprendiendo OOP con Delphi

Pgina 21 de 24

Creado el 17/06/09

Objetos como entidades derivadas: Enlazado dinmico (mtodos virtual y override); mtodos abstractos Objetos com entidades interactuantes: Mensajes polimrficos y comportamiento Patrones: Polimorfismo

ReferenciasLarman (2001) lists Polymorphism as one of his GRASP patterns (pp 326329). Grand (1999) discusses the GRASP patterns, including Polymorphism (pp 6972). Gamma et al (1995) emphasise that the distinction between class and type is very important in OO programming and list several patterns that depend heavily on it (p17). They discuss polymorphic iteration in particular as part of the Iterator pattern (pp 258259).Gamma, E., Helm, R., Johnson, R. and Vlissides, J. 1995. Design Patterns: Elements of reusable object-oriented software. Addison-Wesley, Reading, MA. Grand, M. 1999. Patterns in Java, vol 2. Wiley: New York. Larman, C. 2001. Applying UML and patterns: An introduction to object-oriented analysis and design and the Unified Process, 2nd ed. Prentice Hall: New Jersey.

Aprendiendo OOP con Delphi

Pgina 22 de 24

Creado el 17/06/09

ProblemasProblema 6.1. Estudio Captulo 6Identifique los apropiados ejemplos o secciones del captulo para ilustrar cada comentario hecho en el sumario al final del captulo 6.

Problema 6.2: Sin SustitucinReprograme el ejemplo 6.1 para trabajar sin sustitucin.

Problema 6.3: Orden del testeo de Clases con Enlazado EstticoCon enlazado esttico, Crea alguna diferencia el orden del testeo de clase? Por ejemplo, en el ejemplo 6.2 tendramos alguna diferencia si el manejador de eventos fuera cambiado a lo siguiente? Explique su respuesta.20 procedure TfrmPolymorphism.btnKindClick(Sender: TObject); 21 begin 22 if MyFurniture = nil then 23 lblKind.Caption := 'Object not defined' 24 else if (MyFurniture is TFurniture) then 25 lblKind.Caption := (MyFurniture as TFurniture).GetKind 26 else if (MyFurniture is TChair) then 27 lblKind.Caption := (MyFurniture as TChair).GetKind 28 else if (MyFurniture is TTable) then 29 lblKind.Caption := (MyFurniture as TTable).GetKind; 30 end; // procedure TfrmPolymorphism.btnTypeClick(Sender: TObject)

Problema 6.4. Comparacin entre Enlazado Esttico y DinmicoConvierta el ejemplo 6.3 de dinmico (tardo) a esttico (temprano). Comente las diferencias entre estas dos aproximaciones.

Problema 6.5. Necesidad de un Mtodo AbstractoEn el ejemplo 6.4 creamos un mtodo abstracto en TMyShape. Un mtodo abstracto no tiene una implementacin concreta de s mismo y debe ser implementado separadamente en cada subclase. Por qu la declaramos entonces?Por qu no eliminamos simplemente la declaracin abstracta y virtual GetKind que es parte de TmyShape? Si no est seguro de esto, intente comentar las lneas 7 y 8 de la unit MyShapesU y ver qu ocurre.

Problema 6.6. Remuneracin para EmpleadosCree un programa polimrfico con tres subclases de empleados: fijos, mensuales y por contrato. Todos los empleados fijos reciben 2,500.00 por semana. Los empleados por horas reciben 50.00 por hora para las primeras catorce horas trabajadas en una semana y 60.00 la hora por cada hora extra. Los contratos de tipo 1 son por valor de 1,000.00 y los de tipo 2 por 2,500.00. La pantalla principal del programa debe proporcionar alguna forma de que el usuario pueda seleccionar el tipo de empleado (como en la Figura 13). Para los empleados fijos el programa simplemente displaya la respuesta. Sin embargo, para los otros dos el usuario debe proporcionar ms informacin (p.ej., Figura 14). El cdigo de programa para stos dilogos de entrada debe aparecer en los mtodos que calculan la remuneracin.

Aprendiendo OOP con Delphi

Pgina 23 de 24

Creado el 17/06/09

Figura 13: Seleccionando el tipo de empleado

Figura 14: Introduciendo las horas trabajadas semanalmente por los empleados

Problema 6.7. Ejemplos AdicionalesImagine que est controlando a varios nuevos programadores. Ellos han trabajado con todos los ejemplos dados hasta ahora pero an estn inseguros respecto algunos conceptos. Trabaje con ejemplos adicionales para ilustrarles sobre: a) Los mtodos virtual / override, y b) los mtodos abstractos.

Aprendiendo OOP con Delphi

Pgina 24 de 24