Temas:
Motivación
Los objetos de las clases Box, Ellipse y Text representan figuras geométricas. Estos objetos se crean por ejemplo mediante:
Todos estos objetos poseen implementaciones distintas porque
son de naturaleza distinta. Sin embargo, se puede distinguir un
grupo de operaciones comunes a todos ellos:
Box box= new Box(30, 30, 30, 15); // x, y, ancho, alto
Ellipse eli= new Ellipse(20, 20, 30, 10);
// xcentro, ycentro, rhoriz, rvert
Text text= new Text("palabra", 10, 10);
// string, x, y (ezquina sup. izq.)
Ejemplo | Significado | Encabezado |
---|---|---|
box.moveTo(20, 30); | Mueve la figura geométrica de modo que el punto de referencia quede en (20,30) | moveTo(int x, int y) |
int x= eli.getX() | Obtiene la coordenada x del punto de referencia | int getX() |
int y= text.getY() | Obtiene la coordenada y del punto de referencia | int getY() |
Debe entenderse que cada una de las operaciones se aplica a cualquera de estos objetos. Por ejemplo, moveTo se puede invocar también con la elipse o el texto.
Para dibujar estos objetos en la pantalla se dispone de la clase Animator. Esta clase admite las siguientes operaciones:
Ejemplo | Significado | Encabezado |
---|---|---|
Animator anim= new Animator("Figuras geometricas"); | Crea una ventana en donde se observarán las figuras geométricas. El título de la ventana será "Figuras geometricas". | Animator(String titulo) |
anim.push(box); | Coloca la caja box en la ventana anim. | void push(Glyph glyph) |
anim.push(eli); | Coloca la elipse eli en la ventana anim. | |
anim.push(text); | Coloca el texto text en la ventana anim. | |
anim.sleep(50); | Dibuja todos las figuras geométricas y hace una pausa de 50 milisegundos | void sleep(int milis) |
Observe que el parámetro que recibe la operación push es de tipo Glyph.
Problema 1
Escribir un procedimiento que desplace la caja hacia un punto (x,y) a una cierta velocidad expresada en pixels por segundo. El procedimiento será invocado por ejemplo mediante:
Solución: mover la caja a intervalos de 50 milisegundos. La caja
aparentará moverse en forma continua, de la misma forma que
una imagen en la televisión parece ser continua a pesar de que
se dibujan 30 cuadros por segundo.
mover(anim, box, 120, 100, 80);
Problema 2
void mover(Animator anim, Box box, int xf, int yf, int v) {
double xi= box.getX();
double yi= box.getY();
double dx= xf-xi;
double dy= yf-yi;
double d= sqrt(dx*dx+dy*dy);
double vx= v*dx/d;
double vy= v*dy/d;
double tf= d/v;
double t= 0;
double dt= 0.05;
while (t<tf) {
box.moveTo(trunc(xi+t*vx+0.5), trunc(yi+t*vy+0.5));
anim.sleep(trunc(dt*1000));
t+= dt;
}
anim.sleep(trunc((tf-t)*1000));
box.moveTo(xf, yf);
}
Escribir un procedimiento similar que desplace la elipse.
Solución:
Se podría escribir un nuevo procedimiento mover que ahora recibiese un objeto de la clase Ellipse. Pero lo mismo habría que hacer si se quisiese mover un texto. La programación orientada a objetos permite escribir una sola solución para el problema de mover las distintas figuras geométricas. Esta solución se base en subclases.
Con este procedimiento es posible escribir:
void mover(Animator anim, Glyph glyph, int xf, int yf, int v) {
double xi= glyph.getX();
double yi= glyph.getY() ;
...
while (t<tf) {
glyph.moveTo(trunc(xi+t*vx+0.5), trunc(yi+t*vy+0.5));
anim.sleep(trunc(dt*1000));
t+= dt;
}
anim.sleep(trunc((tf-t)*1000));
glyph.moveTo(xf, yf);
}
Aparentemente, aquí habría un error porque un objeto que pertenece
a la clase Box no puede usarse como argumento cuando lo que se
espera es un objeto de la clase Glyph. Sin embargo, en este caso
box, eli y text también pertenecen a la clase Glyph, porque
Box, Ellipse y Text son subclases de Glyph.
mover(anim, box, 120, 100, 80);
mover(anim, eli, 200, 100, 100);
mover(anim, text, 100, 200, 70);
Definición: Subclases Cuando una clase X es una subclase de Y, todos los objetos de la clase X también pertenecen a la clase Y y por lo tanto se pueden colocar en cualquier lugar en donde es válido colocar objetos de la clase Y.
Por otra parte, cuando X es una subclase de Y, todas las operaciones que admiten los objetos de la clase Y, también son aplicables a los objetos de la clase X. La inversa normalmente no es cierta. Los objetos de la subclase pueden poseer operaciones que no están en otros objetos de la clase. Por ejemplo, los objetos de la clase Box poseen el método setDim que cambia el tamaño de la caja:
La siguiente instrucción no es válida porque setDim no es una operación
válida para objetos de la clase Text.
box.setDim(60, 30); // ancho, alto
Desde un punto de vista de teoría de conjuntos, si denotamos por Box,
Ellipse, Text y Glyph los conjuntos de objetos de cada clase, entonces
Box, Ellipse y Text son subconjuntos de Glyph.
text.setDim( ... ); // error!
porque box también es un Glyph. Esto se denomina una proyección
y consiste en asignar a una variable de tipo Y, un objeto de una
subclase de Y.
Glyph gl= box;
Normalmente, la asignación inversa (que no es una proyección) es un error:
porque el compilador no puede garantizar que el objeto referenciado
por la variable gl sea efectivamente de la clase Box. Java garantiza
en compilación o en ejecución que una variable de tipo Box sólo
referencia objetos de la clase Box y que una variable de tipo
Glyph sólo referencia objetos de la clase Glyph. En el caso de
la proyección, la asignación se puede hacer porque un objeto
de la clase Box también es de la clase Glyph.
Box box2= gl; // error!
Al realizar una proyección se ocultan las operaciones específicas del tipo inicial. Por ejemplo:
Glyph gl= box; // proyección
gl.setDim(30, 15); // error! setDim no es válida sobre un Glyph
La forma general es:
Glyph gl= new Box(...);
...
if (gl instanceof Box) { // verdadero
...
}
if (gl instanceof Ellipse) { // falso
...
}
Es importante hacer notar que aún cuando se tenga seguridad de que el objeto pertenece a la clase Box, todavía no se puede invocar sus operaciones específicas.
if (gl instanceof Box) { // verdadero
gl.setDim(30, 15); // error
}
if (gl instanceof Ellipse) { // falso
...
}
Observe que al conocer un objeto por su clase más específica (su subclase),
es posible invocar las operaciones de la subclase (setDim en el ejemplo).
Glyph gl= ...;
...
if (gl instanceof Box) { // verdadero
Box box2= (Box)gl;
box2.setDim(30,15);
...
}
if (gl instanceof Ellipse) { // falso
Ellipse eli2= (Ellipse)gl;
...
}