Académique Documents
Professionnel Documents
Culture Documents
5 El Parmetro
Sender y la Sustitucin
Conceptos Principales
Sustitucin El parmetro Remitente (Sender) Conversin (Typecasting) Operadores de clase e instancia RTTI: tipo de informacin en tiempo de ejecucin
Pgina 1 de 12
Creado el 17/06/09
Introduccin
Una consecuencia importante de una jerarqua de herencias es el concepto de sustitucin, donde una instancia de un objeto subclase puede sustituir a una variable declarada como cualquiera de sus supertipos. En ste breve captulo introduciremos el concepto de sustitucin investigando el parmetro Sender, que es parte de la lista de parmetros de cada manejador de eventos. Diferentes eventos tienen diferentes listas de parmetros pero el primero en la lista para todos los eventos es Sender: TObject, el cual identifica al componente que ha iniciado el evento. La identificacin del evento iniciador es una idea til, pero no completamente sencilla. La dificultad es que cada parmetro debe tener un tipo, y por tanto Sender debe ser declarado como un tipo especfico. Pero Qu tipo debera ser Sender? Cada componente es diferente. Como veremos en el ejemplo que sigue, Delphi usa la jerarqua de la herencia para resolver ste problema.
Pgina 2 de 12
Creado el 17/06/09
Si intenta esto comprobar que no funciona. El compilador retorna un error Undeclared identifier. Por qu? En la lista de parmetros en la cabecera de ste manejador de evento OnClick, y en todos los dems manejadores de eventos, el tipo para Sender es definido como TObject, la clase raz de la jerarqua, y no como TSpeedButton (lnea 28). La clase TObject no tiene una propiedad Name, y por tanto, cuando el compilador busca Sender.Name en la lnea 30, sta genera un error. Debido a que Sender es un parmetro completamente general, ste es declarado como tipo TObject. Sin embargo, en ste caso sabemos que Sender es un TSpeedButton y por tanto podemos instruir a Delphi para que trate a Sender como un TSpeedButton -en otras palabras, podemos convertir (typecast) Sender en un TSpeedButton. Hay varias formas para hacer esto. Aqu usamos el operador de clase as como sigue (lnea 31):
28 procedure TfrmSender.spdOneClick(Sender: TObject); 29 begin 30 pnlReport.Caption := 'You clicked on ' + 31 (Sender as TSpeedButton).Name; 32 end; // procedure TfrmSender.spdOneClick
Este programa compila y funciona como la primera versin, as que ahora estamos usando el parmetro
Sender correctamente.
Hemos visto al operador de conversin as en el ejemplo 2.1, paso 2, lnea 17, y aqu lo miraremos ms detenidamente. Esta conversin informa a Delphi que aunque Sender es declarado en la lista de parmetros como un TObject podra ser cualquiera de un nmero de distintas clases, y en ste particular caso Delphi debe tratarlo como un TSpeedButton (lnea 31 arriba).
Pgina 3 de 12
Creado el 17/06/09
Usando la pestaa de Eventos del Inspector de Objetos, enlace los eventos OnClick de pnlControl, y frmSender al manejador de eventos GeneralClick (utilizamos una condicin incluso en la ltima clusula else para asegurarnos que responder slo a TPanels. Cualquier cosa excepto un TSpeedButton, un TForm o un TPanel es ignorado). Ejecute y testee sta versin de programa y compruebe que responde correctamente a los clicks en los paneles y el formulario as como en los SpeedButtons. No responde a un click sobre lblInheritance aunque lo hayamos enlazado a GeneralClick como su manejador de eventos, ya que GeneralClick no tiene condicin If para testear un TLabel.
pnlReport, lblInheritance
Pgina 4 de 12
Creado el 17/06/09
TSpeedButton, TForm y TPanel son todos descendientes de TComponent, eso es por lo que ellos tienen todos la propiedad Name, y el TComponent es a su vez un descendiente de TObject. Podemos aprovechar esto para nuestro programa trabajando a nivel de TComponent en vez de clases individuales (lneas 3032
debajo):
28 procedure TfrmSender.GeneralClick(Sender: TObject); 29 begin 30 if Sender is TComponent then 31 pnlReport.Caption := 'You clicked on ' + 32 (Sender as TComponent).Name 33 end; // fin TfrmSender.GeneralClick
Almacene y ejecute sta versin de programa. Da los mismos mensajes que la del paso 4 si hacemos click sobre un SpeedButton, un panel o el formulario, y ahora tambin responde a un click sobre lblInheritance. Aqu obtenemos un nuevo beneficio desde la herencia. Debido a que TSpeedButton, TPanel, TLabel y son todos descendientes de TComponent, podemos probar que sean un TComponent (lnea 30) o desciendan de un TComponent (lnea 32) en lugar de probar y convertir cada tipo de componente por separado como en el paso 4.
TForm
Donde estamos usando una propiedad (o mtodo), en la que las subclases heredan desde una superclase, podemos trabajar con la superclase en vez de con cada subclase individualmente. Esto nos ahorra programacin, ya que no tenemos que atender separadamente para cada subclase de la clase y as podemos evitar situaciones como la del paso 4, donde tenemos un estamento If separado para cada subclase. Tambin significa que si a futuro introducimos una subclase ms lejana no tenemos que buscar a travs de todo el programa para atenderla especficamente a ella. Lo que vemos en ste paso es importante. Sender, de tipo TObject, es lanzado como descendiente de un TComponent, el cual a su vez es un ancestro de varios otros objetos usados aqu (TspeedButton, TPanel y TForm). Como todos estos objetos heredan la propiedad Name de su ancestro TComponent, podemos lanzar Sender como a su ancestro TComponent. Esto nos permite trabajar con ellos como grupo en lugar de con cada uno individualmente. Aunque estamos trabajando con un TComponent en las lneas 30 y 32, cualquiera de sus descendientes (aqu TSpeedButton, TPanel o TForm) puede sustituirlo, comportndose como si fueran un TComponent. Este es un ejemplo de sustitucin.
La notacin mediante parntesis se parece un poco a una llamada a subrutina y no realizar ningn chequeo de errores. An as, aunque es algo menos eficiente, muchos programadores la prefieren como operador de conversin ya que es ms robusta y fcil de leer.
Pgina 5 de 12
Creado el 17/06/09
Pgina 6 de 12
Creado el 17/06/09
El diagrama de clase muestra en la clase B slo los campos de datos adicionales declarados en la clase B, con la flecha de generalizacin desde la clase B a la clase A indicando que la clase B tambin tiene campos de datos declarados en la clase A. Sin embargo, una instancia de la clase B debe tener memoria reservada para los Campos de Datos A y los Campos de Datos B (y similarmente para las otras clases). Digamos que tenemos una instancia de la clase A, dos instancias de clase Class B, una instancia de clase C y una instancia de clase D. Podemos representar esto como sigue:
Cuando una instancia de clase B, C o D sustituye para una instancia de clase A, la estructura de subclase adicional (Campos de Datos B, C y/o D) son ignorados. Esto es posible porque el solapamiento en la estructura entre una superclase y todos sus descendientes. Sin embargo, la clase B no puede sustituir a la clase C o viceversa por las diferencias en sus estructuras. Una instancia de la clase D puede sustituir instancias tanto de la clase B como de la clase A (pero no para la clase C). Nos hemos referido slo a los campos de datos de los objetos hasta ahora. Un concepto similar pero ms sofisticado se aplica a los mtodos y, donde tenemos enlaces en tiempo de ejecucin, hace posible el polimorfismo. El polimorfismo es un concepto intrigante, y dedicaremos el prximo captulo a l.
Pgina 7 de 12
Creado el 17/06/09
Pgina 8 de 12
Creado el 17/06/09
Este es un breve captulo que simplemente ilustra el concepto de sustitucin para preparar el camino al polimorfismo. En el prximo captulo exploraremos la generalizacin y la sustitucin, sta vez con clases escritas por el programador, y desarrollaremos el concepto para incluir polimorfismo. El polimorfismo es fundamental para varios de los patrones que investigaremos ms tarde y es una de las principales razones para el crecimiento y desarrollo de la programacin orientada a objetos.
Pgina 9 de 12
Creado el 17/06/09
Problemas
Problema 5.1. Estudio de Captulo 5
Identifique los ejemplos o secciones adecuados del captulo para ilustrar cada comentario hecho en el sumario anterior.
Los manejadores de eventos para todos los objetos de la interfaz de usuario son enrutados a travs del siguiente manejador de eventos:
23 procedure TfrmSender.GeneralClick(Sender: TObject); 24 var 25 Class1, Class2: TClass; 26 ClassStr: string; 27 begin 28 Class1 := Sender.ClassType;
Pgina 10 de 12
Creado el 17/06/09
29 30 31 32 33 34 35 36 37
Class2 := Class1.ClassParent; while Class2 <> nil do begin ClassStr := Class1.ClassName + ' derived from ' + Class2.ClassName; lstClasses.Items.Add(ClassStr); Class1 := Class1.ClassParent; Class2 := Class1.ClassParent; end;
a) Explique cmo ste manejador de eventos funciona y qu hace, refirindose a los principios cubiertos en el captulo 5. b) Dibuje el diagrama de clase UML para todas las clases en ste programa sobre las bases de la operatoria del programa.
Pgina 11 de 12
Creado el 17/06/09
Ese panel de control proporciona control independiente de tres semforos distintos. Cada luz de trfico puede ser configurada automtica o manualmente va un CheckBox. Cuando el CheckBox es chequeado la luz de semforo asociada funciona automticamente (4 ciclos rojo, 3 ciclos verde, 1 ciclo amarillo). Cuando el CheckBox est deseleccionado, el botn Step es activado. Cada click en el botn Step mueve la luz de trfico asociada, independientemente de las otras dos, al siguiente color en la secuencia. El tercer control para cada luz, un SpinEdit, da la longitud de ciclo en dcimas de segundo. As que si SpinEdit se establece a 8, la luz que controla pasa 3.2 segundos en rojo, 2.4 en verde y 0.8 en amarillo. Cualquier cambio de valor del SpinEdit toma efecto desde el siguiente cambio de color. Codifique ste programa, incorporando los principios OO cubiertos hasta ahora como considere apropiado. Incluya documentacin UML simple. Hay varias formas de atacar a ste problema. Los programadores menos experimentados podran encontrar tiles los siguientes pasos: 1. Escriba una simple descripcin de algoritmo de bajo nivel para cada uno de los siguientes casos: a) Active una luz de trfico para operacin automtica chequeando un CheckBox. b) Desactive el paso automtico y hgalo manualmente a travs de la secuencia pulsando sobre un botn. c) Prepare un CheckBox para operacin automtica y luego cambie el ciclo temporal. 2. Desde stas descripciones, decida qu clases son necesarias y cmo deberan ser relacionadas. 3. En base a las clases del diagrama de clases, dibuje un diagrama de secuencia para cada una de las clases anteriores. 4. Implemente cada caso. Probablemente es ms sencillo no resolver todo el problema de una sla vez. En lugar de eso, puede decidir comenzar slo con el controlador y con una sla luz de trfico y concentrarse en cambiar la operacin automtica de dicha luz de on a off correctamente. Entonces tendra la posibilidad de elegir. Podra tener las tres luces controladas independientemente. Entonces podra obtener control manual trabajando con una luz y extenderlo a las tres, y finalmente programar el ciclo ajustable para una y extenderlo a las tres. Alternativamente, podra seguir con una sla luz y obtener el manual de paso para funcionar, seguido del ciclo ajustable, y luego finalmente expandirlo al control independiente de las primeras dos luces y luego a las tres luces. 5. Una vez el programa est escrito, testeado y funcionando, dibujae un diagrama de clases UML, mostrando el panel de control maestro, los displays de luces de trfico, las clases de luces de trfico y cualesquiera otras clases significativas y sus relaciones entre clases, y la secuencia de diagramas mostrando la operacin para cada uno de los casos. 6. Prepare una breve descripcin sobre cmo el programa funciona e identifique los principios OO que ha includo.
Pgina 12 de 12