Logo: Tutorial POO
Este tutorial está destinado a programadores iniciados en Lingo. La intención es llegar a conocer los principios de la programación con objetos. La POO es algo difícil de describir y esto hace que muchos programadores piensen que esté reservada a expertos en programación. Realmente, es fácil y una vez se entienden los conceptos básicos nos resultará muy útil para casos concretos. El curso está escrito de la manera más práctica que he sabido, planteando ejemplos de todo lo que voy explicando que, cómo no, te aconsejo vayas practicando al mismo tiempo que haces la lectura.

Te aviso que el principio del tutorial puede parecer algo espeso. Como siempre pasa, la teoría es bastante aburrida pero una vez aclarado algún que otro concepto, el resto del curso es bastante práctico y entretenido o, eso pienso al menos :-). Con un poco de paciencia y con la ayuda de los ejemplos comprenderás bastante bien los principios de la POO. ¡Suerte!

Parte 1

Qué es un objeto

Un objeto es una instancia de un script padre que contiene datos (propiedades) y/o instrucciones. A partir de un script padre podemos sacar todos los objetos que queramos y estos tendrán las mismas características que el padre pero sus propias variables (propiedades) que le harán comportarse de forma diferente. Esto quizás se entenderá mejor con algunos ejemplos:

Imaginemos un script padre llamado Vehiculo con 2 propiedades: Revisión y Kilómetros. En este script padre se podría añadir la condición de que si la propiedad Kilómetros es mayor de 5000, Revisión tendrá valor TRUE y tenemos que llevar el vehículo al taller. Teniendo esta base, podemos sacar multiples copias, o sea, objetos hijo que responden de la misma forma pero con sus propias variables.

Otro caso para utilizar objetos son los juegos arcade porque podemos crear un script padre que defina a un marciano o comecocos con sus propiedades y rutinas de movimiento, disparo, etc y crear tantos objetos como bichos queramos. Este es uno de los ejemplos más claros y donde mejor se justifica el uso de POO.

Crear un script padre

Ahora que ya tenemos una idea de qué es un objeto y un script padre vamos a crear el script padre mínimo y conoceremos así su estructura.

Abrimos la ventana script desde el menú Window/Script, después pulsamos el botón I (cast member properties) y seleccionamos el tipo Parent. Le damos nombre al script desde la ventana cast y ya solo falta escribir el código de nuestro script padre.

Ej. de script padre mínimo:

on new me
return me
end
La estructura mínima de un objeto la forma el handler o función new (en Director 4 o anterior se usa la función birth, alumbrar en inglés ) seguido del parámetro me que es un puntero o número de identificación ( en adelante ID ) del objeto. Cada objeto que creemos tiene un número de ID único que, normalmente, se guarda en una variable o en una lista para cuando queramos referenciar al objeto.

Para alumbrar el objeto hacemos una llamada a la función new que devuelve un valor de retorno ( el ID ) y éste lo guardamos en una variable:

set mivar = new (script "nombrescriptpadre")
Con esto ya hemos creado un objeto. Si queremos crear otro objeto del mismo script padre repetimos la operación pero con otra variable:
set miotravar = new (script "nombrescriptpadre")
Cuando queramos crear un número grande o indeterminado de objetos, a partir del mismo script padre, lo más cómodo es utilizar una lista. Cada elemento de la lista contendrá un objeto, es decir el ID.

Si queremos crear 100 objetos el script a utilizar podría ser este:

set milista=[]
repeat with n=1 to 100
add milista,new (script "nombrescriptpadre")
end repeat
Para utilizar un objeto en concreto utilizamos la función getat que devuelve el valor de una posición de la lista. Te recuerdo la sintaxis: getat (lista,posición).

Propiedades de un objeto y parámetros iniciales

Vamos a aplicar lo dicho hasta ahora en un caso más práctico (encontrarás el listado completo en el archivo Poo1.dir). Volviendo al ejemplo del script padre Vehículo creamos su script y lo preparamos para recibir valores iniciales. Te recuerdo que tiene 2 propiedades Revisión y Kilómetros y, además, si Kilómetros es mayor de 5000 cambiaremos el estado de la propiedad Revisión. Esto último se consigue a través de la función Taller que está dentro del propio script padre:
-- Parent Script Vehiculo
property Revision
property Kilometros

on new me, tRevision, tKilometros
set Revision = tRevision
set Kilometros = tKilometros
return me
end

on Taller me
if Kilometros > 5000 then set Revision = TRUE
end
-- Fin de Parent Script Vehiculo

Para crear objetos con unos valores iniciales le mandamos a la función new los parámetros:
set coche1 = new (script "Vehiculo", FALSE, 0 )
Como ya habrás deducido, después de ejecutar esta instrucción, coche1 guarda el valor de ID de este objeto, Revisión se inicializa a False y Kilómetros a 0.
set coche2 = new (script "Vehiculo", FALSE, 1245 )
Para comprobar si tenemos que llevar algún coche al mecánico llamamos a la función Taller seguida del ID del objeto que nos interese:
Taller coche1
Taller coche2

Cómo cambiar los datos del objeto

Para que esta simulación de coche sea más real, durante la ejecución del programa incrementamos a voluntad la propiedad Kilómetros. (Atencián al artículo the antes de la propiedad).
set the kilometros of coche1 = (the kilometros of coche1)+1
Del mismo modo, si queremos consultar alguna propiedad del objeto:
put the Kilometros of coche1
put the Revision of coche1

Eliminar un objeto y los contadores de referencia

Cuando terminamos de usar un objeto, se debe liberar para recuperar la memoria que ocupa. Para ello, igualamos a 0 cualquier variable que contenga el ID.

Lingo tiene un contador interno de las variables que apuntan a un objeto. Si imprimimos en la ventana Message el valor de coche2 obtentremos algo así:

-- put coche2
<offspring "Vehiculo" 2 27a9262>

  • offspring "Vehiculo" indica que es un hijo de Vehículo
  • 2 indica que hay 2 variables que apuntan a Vehículo, una es el propio objeto en sí y otra la variable coche2. Si copiamos coche2 a otra variable valdrá 3, del mismo modo si igualamos a 0 esta variable se decrementa en 1. El objeto sólo será liberado cuando coche2 valga 1, que es cuando ninguna variable apunta al objeto excepto el objeto en sí, de ahí el valor 1.
  • 27a9262 es una dirección de memoria en hexadecimal.
En este ejemplo si queremos borrar el objeto coche2 hacemos:
set coche2=0
Consulta Poo1.dir donde está el programa completo.

Parte 2

The actorList y stepFrame

Si creamos una lista de objetos denominada the actorList, cuando se entre en un frame cada uno de los objetos recibirá un mensaje stepFrame. De esta forma, si hemos puesto en el script padre una función on stepFrame, se ejecutará el contenido de ésta.

Para que se entienda mejor, pondré un ejemplo. El programa completo está en Poo2.dir. Vamos a crear un objeto que simule el comportamiento de una pelota botando:

-- Parent Script Pelota
property dir
property numSpr
on new me,tdir,tnumSpr
set dir = tdir
set numSpr = tnumSpr
puppetsprite numSpr,true
return me
end

on stepFrame
cambiarDir
Mover
end

on cambiarDir
if the locV of sprite numSpr < 50 then
set dir = 5
else if the locV of sprite numSpr > 240 then
set dir = -5
end if
end

on Mover
set the locV of sprite numSpr = ( the locV of sprite numSpr ) + dir
end
-- Fin de Parent Script Pelota

Para crear un objeto dentro de la actorList:
add the actorList,new (script "pelota", -5,1)
Para acabar el programa en la celdilla 1 del Score, pegamos el dibujo de una pelota en las coordenadas 320,240, en el script de frame del frame 1 ponemos un go to the frame, en el script startMovie creamos el objeto tal como ya hemos visto y, por último, en el script stopMovie vaciamos el contenido de la actorlist ( set the actorList=[] ). No te preocupes por todo esto, en el archivo Poo2.dir lo tienes hecho.

Al hacer Play la pelota botará hasta que hagamos Stop. Cada vez que entramos en el frame 1, se ejecuta la función stepFrame que hace botar el sprite 1 en la dirección que indica la variable dir. Inicialmente, dir vale -5 para que la pelota empiece el movimiento de abajo a arriba.

Parte 3

Hijos, padres y abuelos

Una posibilidad, que no hemos tratado todavía, es la herencia de los objetos. Gracias a esto, podemos crear un nuevo tipo de objeto a partir de otro ya existente. Este nuevo objeto, hijo del anterior, hereda todas las carectísticas de su padre y, además, tendrá las suyas propias. No hay límite para el número de generaciones. Cada hijo puede tener, a su vez, otro hijo.

El ejemplo típico para utilizar ancestros sería el de un juego matamarcianos, donde tenemos un tipo de marcianos con unas caractistícas y acciones definidas como por ejemplo, disparar, cambiar de color, moverse, etc. Pero además, para este juego diseñamos un segundo tipo de marciano, hijo del anterior, con todas sus características heredadas, más alguna propia que será única. Por poner un ejemplo, es más rápido y su disparo más efectivo.

Una de las ventajas de usar ancestros está en el ahorro de reescribir el código y en la disminución de la posibilidad de errores. Seguramente, al desarrollar cualquier aplicación encontraremos una manera de evitar el uso de ancestros. Personalmente, nunca he tenido necesidad de usar más de un hijo de un objeto y, cuando lo he hecho, ha sido más por prácticar estos temas que porque fuera necesario, ya que me resulta más claro trabajar sin herencias. De todas formas, ya que existe esta opción, vamos a tratarla un poco aprovechando para ello el objeto Pelota que ya tenemos creado en el capítulo anterior. Quizás no sea un caso muy real y se podría hacer de una forma más directa, pero como ejercicio didáctico ya sirve.

Crear un hijo

Para crear un objeto hijo en su script padre, hay que definir la propiedad ancestor y después guardar aquí el ID del script padre de este objeto . En el siguiente ejemplo se crea el script mínimo ancestro de otro que, como ya hemos dicho, hereda todas las propiedades y funciones del scriptpadre.
-- script hijo
property ancestor
on new me
set ancestor = new (script "scriptpadre")
return me
end
El padre de este script, al que pondremos una propiedad llamada Nombre, podría ser el siguiente:
-- script padre
property Nombre
on new me
set Nombre="Pedro"
return me
end

La herencia de las propiedades

La propiedad Nombre, aunque solo está definida en el script padre, también existirá en los objetos hijo. Al arrancar el programa, padre e hijo comparten el valor de la propiedad Nombre pero se puede cambiar de forma independiente. Para hacer esto, es imprescindible conocer el ID de cada objeto.

Si se quiere cambiar el valor de una propiedad, desde fuera del script, se hace como ya hemos visto:

set the Nombre of hijo = "Otronombre"
set the Nombre of padre = "Sinnombre"
siendo hijo y padre las variables que contienen el ID.

Para inicializar Nombre al crear el objeto, modificamos el script hijo como sigue:

property ancestor
on new me
set ancestor = new (script "padre")
set the Nombre of me="Tunombre"
return me
end
Esto funciona porque me contiene el ID de sí mismo y, al crear el objeto, se modifica el valor de Nombre.

El fichero que ilustra esta parte está sin acabar. Te propongo que abras Poo3.dir y desde la ventana Message crees tú mismo los objetos y practiques las herencias de propiedades.

Parte 4

Compartir Funciones

En el capítulo anterior, creamos el objeto Pelota que contenía un sprite que se mueve en el eje vertical. Ahora, a partir de éste, vamos a crear Pelota2 que se mueve en los dos ejes, vertical y horizontal, haciendo un irreal bote en diagonal. Ya que tenemos las funciones Mover y cambiarDir de Pelota, para añadir al hijo el movimiento horizontal, solo tenemos que programar la función Diagonal porque el resto lo heredamos. (El programa completo está en Poo4.dir)

Aquí tienes el script de Pelota2:

-- Parent Script Pelota2
property ancestor

on new me
set ancestor = new (script "pelota",-5,2)
return me
end

on stepFrame
cambiarDir (ancestor)
Mover (ancestor)
Diagonal
end

on Diagonal
set the locH of sprite 2 = (the locH of sprite 2) + (the dir of ancestor)
end
-- Fin de Parent Script Pelota2

Las funciones cambiarDir y Mover no están definidas aquí pero las usamos como si fueran propias. Para que Lingo pueda encontrarlas, hay que decirle el ID del objeto que las contiene y, para ello, nos valemos de la propiedad ancestor.

Para hacer el incremento positivo y negativo de la coordenada horizontal, nos aprovechamos de la propiedad dir. En este caso, usamos el valor del objeto padre haciendo una lectura directa.


Pulsa aquí para bajar el Tutorial de POO + Ejemplos
©1998 Pedro Agriarte
e-mail:[email protected]
URL:http://www.geocities.com/yosemite/rapids/6716