Beruflich Dokumente
Kultur Dokumente
la introduccin de ambigedad
103
Sobre el captulo
Motivaciones
La herencia ha sido y es un canto de sirena en los objetos. Quien lo escucha con
odos ingenuos se hunde con el sistema. Parece que la herencia es la fuente de ahorro de
cdigo, de la facilidad de modificacin y extensin de los programas, pero realmente es
lo contrario, salvo que se utilice con el papel y la forma adecuada. El papel adecuado de
la herencia es como medio de aumento de la ambigedad y la forma adecuada de uso es
mediante el polimorfismo.
Objetivo
El objetivo del presente captulo es que los alumnos comprendan:
1. El concepto de herencia, su papel favorable y sus papeles perjudiciales
2. El concepto de polimorfismo
3. Que es mejor delegar que heredar
4. El principio de sustitucin de Liskov, como forma segura y til de la
herencia
5. Las clases abstractas
6. Que la ambigedad es la clave, no la divisin
Contenido
La primera parte profundiza en la herencia, sus aspectos buenos y malos, y
recomienda que es preferible delegar que heredar. Se enuncia tambin el concepto de
polimorfismo.
La segunda parte estudia con detalle el principio de sustitucin de Liskov, sus
condiciones y cmo un cuadrado no es un rectngulo, en trminos de la herencia
software.
La tercera parte se dedica a las clases abstractas; a la ambigedad como solucin
de la diversidad y, en general, como solucin ms poderosa que el limitado principio de
divide y vencers.
La cuarta parte discute la herencia mltiple, sus efectos nefastos actuales y las
restrictivas condiciones donde es favorable, por ejemplo, en el patrn adaptador.
104
4.1
La herencia
La herencia es uno de los cantos de sirenas del enfoque de objetos. Se dice
mucho de sus favores y muy poco de sus peligros. Por esta causa primero se estudiar la
definicin y los peligros, y despus se ver la forma de obtener los favores de la
herencia.
Desde el punto de vista formal, [Booch 94] establece que:
La herencia es una relacin entre clases donde una clase comparte la estructura o
comportamiento definido en otra clase (herencia simple) o en ms clases
(herencia mltiple). La herencia define una jerarqua es-un entre clases, en la
cual una subclase hereda de una o ms clases generalizadas; una subclase
tpicamente especializa su superclase aadiendo o redefiniendo la estructura y el
comportamiento.
La Figura 4. 1 ilustra el mecanismo de herencia simple.
Figura 4. 1 La herencia
4.2
El polimorfismo
El polimorfismo es una de las cualidades importantes del enfoque de objetos por
declaracin de la variable v
v O:A
v:m2
v es A
declaracin de la variable v
v O:C
v.m2
declaracin de la variable v
v O:B
v.m2
declaracin de la variable v
v O:C
v.m4
4.3
variantes de A. Esta idea permite asociar la herencia con una relacin jerrquica es un.
Es decir, como un instrumento de clasificacin, al estilo taxonmico de las ciencias
naturales. Abundan los ejemplos que ilustran la herencia a travs de modelos
taxonmicos: la clase perro es una subclase de la clase mamfero. Pero, en general,
constituye un ejemplo poco afortunado. Primero, porque cualquier clasificacin es
subjetiva y siempre admite objeciones, de manera que al introducir clasificaciones en el
107
4.4
4.5
software, que se ajusta a las nuevas necesidades, a travs de la creacin de clases hijas
que heredan las propiedades de las clases existente y las modifican o aaden otras
propiedades nuevas. Parece que la herencia permite ahorrar cdigo, no tocar lo que ya
funciona, y adems, permite que se puedan cambiar muchas clases cambiando una sola.
Son ideas atractivas, como las sirenas.
La Figura 4. 2 muestra un rbol jerrquico de herencia cuya raz es la clase
Madre. De ella derivan las clases Hija 1 e Hija 2, que a su vez tienen hijas: la clase
Nieta 1 y Nieta 2, Para compactar la figura, los atributos y los mtodos se han separado
por comas, aunque cada uno debe ir escrito en una lnea, segn la notacin de UML.
109
110
Al ver en el cdigo la clase Nieta 2, se puede pensar que slo tiene la propiedad
m2, pero no es as. Como esta clase es hija de Hija 2, habr que buscar y localizar a la
clase madre para saber qu propiedades hereda de ella. Una vez localizada, se sabe que
Nieta 2 tiene la propiedad m2 (propia, porque redefine a la madre) y la propiedad a2,
segn la madre, Hija 2. Pero, la historia continua. Hija 2 es hija de Madre. Entonces,
Nieta 2 no est casi vaca como parece. Nieta 2 tiene las propiedades: m2, propia; a2 de
Hija 2 (su madre, que a su vez redefine a Madre, la madre de Hija 2). Y tiene tambin,
las propiedades a1, a3, m1 y m3 de Madre porque Hija 2 no las redefine.
En fin, con apenas tres clases se pudiera exclamar: Madre, el drama padre,
ttulo de un delicioso enredo teatral de Jardiel Poncela. Mientras ms genealoga, ms
calvario y mayor probabilidad de equivocaciones. Por otra parte, si se quiere reutilizar
alguna clase hija habr que llevarse a la nueva aplicacin, todas las clases antecesoras.
111
4.6
utiliz el ejemplo de una pila y una lista, pero se puede usar el ejemplo de cliente y
persona. Figura 4. 4
112
113
4.7
sustitucin de Liskov, formulado hace casi dos dcadas, ofrece un camino til y
confiable para aprovechar los favores de la herencia.
Literalmente el principio define, en trminos de una sustitucin segura, cuando
una subclase es un subtipo de una superclase.
Si para cada objeto O1 de tipo S hay un objeto O2 de tipo T tal que para todos
los programas P definidos en trminos de T, el comportamiento [interno] de P
no cambia cuando O1 es sustituido por O2, entonces S es un subtipo de T.
[Liskov 86]
La Figura 4. 5 ilustra la aplicacin del principio. Cuando O1:S es sustituido por
O2:T ningn objeto, por ejemplo :N, que espera a O2:T se altera si recibe a O1:S.
114
4.8
115
4.9
ofrecer una forma segura de usarlo. Pero, cul es el lado bueno de la herencia porque
antes de estudiar el principio slo se haban visto lados problemticos. El propio ttulo
del principio ofrece la respuesta: el lado bueno de la herencia es su capacidad de
producir elementos distintos, pero sustituibles. Es decir, la cualidad favorable de la
herencia es su capacidad para elevar la ambigedad en el diseo y as facilitar el manejo
de la complejidad descriptiva y de incertidumbre.
La evolucin, expansin o modificacin del software a travs de la herencia
debe cumplir las condiciones del principio de sustitucin de Liskov para evitar
dificultades.
118
119
121
124
Pero, para qu todo en una sola clase? Una sola clase no es necesariamente ms
simple que varias clases. La complejidad no se reduce por empaquetarla en un solo
elemento. Generalmente sucede al revs. La complejidad de la clase Figura* es mayor
que la complejidad total de las clases separadas y adems es ms embarazosa de
manejar.
Por ejemplo, para que la clase Rectngulo opere con cuadrados hay que darle un
valor al atributo ancho imprescindible para la clase, pero innecesario para el humano.
La clase Paralelogramo exigira adems, el ngulo.
Otra variante sera definir los atributos de Rectngulo en trminos de los vrtices
de la diagonal y el mtodo pintar, de la clase Rectngulo, pintara la figura inscrita en
esos vrtices, sin distinguir cuadrado de rectngulo. La responsabilidad de distinguir se
traspasa a la persona, cliente de la clase Rectngulo, que debe calcular las coordenadas
del otro vrtice, tanto para el rectngulo como para el cuadrado. Si se equivoca en la
operacin de clculo la figura sale mal. La situacin es peor si la clase Paralelogramo
se utiliza tambin para cuadrados y rectngulos. No obstante, puede ser til disponer de
una clase donde la figura se describa a travs de vrtices o puntos.
Comnmente, mientras mayor sea la generalidad de la clase que realiza la tarea,
ms se complica su desarrollo, mantenimiento, manipulacin, en fin todo. El desarrollo
progresivo de una clase como Figura* significa modificar una y otra vez el interior de
la clase y a quienes usan la clase. El desarrollo de una vez obliga a enfrentarse a un
problema mayor que el problema de enfrentarse cada vez con una figura simple. El
mantenimiento para corregir y perfeccionar una clase como Figura* aborda un
mecanismo ms complejo montado en una sola pieza donde todo est relacionado con
todo. De la manipulacin ya se ha hablado. Precisa informacin redundante cuando
opera con casos particulares ms simples, muchas veces exige trabajo extra de quienes
usan la clase y por estas dos causas es ms susceptible a equivocaciones.
La clase Figura* es una clase concreta que implementa una herramienta
general. Es como una piedra que sirve para clavar, cortar, lanzar, dibujar, calzar,segn
se use. Hay que soportar un peso innecesario cuando se usa para cortar y hay que tener
cuidado de no cortarse, al usar la piedra para clavar. Adems, cuando se trate de mejorar
alguno de sus usos, se puede perjudicar otro porque todos ellos estn muy ligados, en la
125
misma piedra. Los humanos fueron especializando los distintos usos de la piedra en
instrumentos particulares. Tambin conviene hacerlo en el software.
126
existencia de la clase Figura describe que el sistema se ocupa de figuras. Sin esta clase
habra que decir que el sistema se ocupa de cuadrados, rectngulos, Adems, la
ambigedad simplifica tambin la complejidad de incertidumbre porque simplifica el
trabajo de reajuste presente y futuro del sistema.
El lenguaje Java adopta esta ltima solucin de forma radical. Slo permite la
herencia mltiple de las denominadas interfaces (clases abstractas puras que contienen
porque suprime los atributos y reduce las operaciones a simples declaraciones. La
implementacin de las operaciones, en clase que hereda, resuelve las colisiones
potenciales. Esta modalidad de la herencia mltiple es una forma segura y til para
dotar a una clase con ms de una cara (interfaz).
131
Las clases Crculo, Rectngulo y Tringulo son distintas entre s porque tienen
atributos distintos y comportamientos distintos: un crculo se pinta de forma distinta que
un rectngulo y que un tringulo; tambin la forma de ampliarlo, reducirlo o moverlo es
distinta a la del rectngulo y el tringulo. Pero todas ellas son figuras, por tanto se podr
disear una abstraccin que exprese la esencia de inters y omita los detalles que las
diferencian. La clase Figura, discutida antes, puede ser un punto de partida para este
diseo particular.
Las cabeceras de las operaciones de cada figura coinciden porque han sido
diseadas con una disciplina, manteniendo un estilo. Si las cabeceras no coincidieran
sera posible aplicar una tcnica de adaptacin para uniformarlas. La coincidencia es
necesaria para conseguir que las operaciones sean polimrficas cuando se relacionen
con las operaciones homnimas de la clase Figura. Figura 4. 19
132
Entre la clase Ventana y la clase Figura existe una relacin de agregacin porque
la clase Ventana contiene un atributo Figura de tipo Figura. Este atributo ser en un
momento dado un objeto Crculo, Tringulo o Rectngulo. Pero como todos comparten
133
la misma especificacin, la clase Ventana puede tratarlos a todos por igual utilizando la
especificacin de Figura, sin necesidad de conocer el tipo de figura, excepto en el
instante de la creacin de los objetos.
Un constructor est ceido a crear objetos de una clase especfica, no puede
crear objetos de otras clases por su especificidad. La creacin de un objeto no es una
operacin polimrfica. De manera que el objeto creador establece una fuerte relacin
unvoca hacia el objeto creado. Para debilitar esta relacin se utilizan diversas tcnicas
segn sea el caso. Algunas de ellas sern vistas en el curso.
La clase Ventana no puede crear objetos de tipo Figura porque Figura es una
clase abstracta. La clase Ventana crear objetos de tipo Crculo, Rectngulo y Tringulo
segn elija, en la pantalla, el usuario del sistema. Por eso hay una relacin de
dependencia de Ventana a cada una de las clases Crculo, Tringulo y Rectngulo. Una
vez que el usuario haya elegido la figura se asignar al atributo figura el objeto creado y
Ventana podr trabajar con l a travs de la especificacin de Figura. Se abre la puerta
al uso del polimorfismo.
Volviendo al principio de sustitucin de Liskov, observamos que en este caso
Crculo, Tringulo y Rectngulo son subtipos de Figura ya que podemos sustituir
Figura por cualquiera de ellos en el cdigo de Ventana y el resultado ser correcto.
134
private JButton circulo, rectangulo, triangulo, ampliar, reducir, arriba, abajo, izqda, dcha;
public Ventana() {
//Pintar la ventana vaca
setTitle("Pintar Figuras");
asignarLookAndFeel();
setCloseClick();
setExtendedState(MAXIMIZED_BOTH);
configurarGUI();
//Repintar la ventana con la figura
pack();
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
private void asignarLookAndFeel()
{
//Forzar el Look and Feel de la ventana al del sistema
String laf = UIManager.getSystemLookAndFeelClassName();
try {
UIManager.setLookAndFeel(laf);
}
catch (UnsupportedLookAndFeelException exc)
{System.err.println("Unsupported: " + laf);}
catch (Exception exc)
{System.err.println("Error cargando: " + laf);}
}
private void setCloseClick()
{
//Controlar el cierre de la ventana
addWindowListener(new WindowAdapter()
{ public void windowClosing(WindowEvent e)
{System.exit(0);}
});
}
private void configurarGUI(){
135
136
dcha.setEnabled(true);
}
if (boton == rectangulo){
figura = new Rectangulo();
ampliar.setEnabled(true);
reducir.setEnabled(true);
arriba.setEnabled(true);
abajo.setEnabled(true);
izqda.setEnabled(true);
dcha.setEnabled(true);
}
if (boton == triangulo){
figura = new Triangulo();
ampliar.setEnabled(true);
reducir.setEnabled(true);
arriba.setEnabled(true);
abajo.setEnabled(true);
izqda.setEnabled(true);
dcha.setEnabled(true);
}
if (boton == reducir)
figura.reducir(zoom);
if (boton == ampliar)
figura.ampliar(zoom);
if (boton == arriba)
figura.mover(0,-desplazamiento);
if (boton == abajo)
figura.mover(0,desplazamiento);
if (boton == izqda)
figura.mover(-desplazamiento,0);
if (boton == dcha)
figura.mover(desplazamiento,0);
this.repaint();
}
public void paint (Graphics g)
{
super.paint(g);
if (figura!=null)
figura.paint(g);
}
public static void main(String[] args) {
new Ventana();
}
Declaramos
la
abstracta Figura
clase
}
public void paint(Graphics g){
137
super.paint(g);
}
Declaramos
Crculo que
Figura
la
clase
hereda de
138
Implementamos el
mtodo paint
centrox=centrox+desplazamientox;
centroy= centroy+desplazamientoy;
}
}
public void ampliar (int zoomin){
Implementamos el
mtodo ampliar
Implementamos el
mtodo reducir
Declaramos
la
clase
Rectngulo que hereda de
Figura
Implementamos el
mtodo paint
139
g.setColor(color);
g.drawRect(origenx,origeny,base,altura);
g.fillRect(origenx,origeny,base,altura);
g.dispose();
}
}
public void mover (int desplazamientox, int desplazamientoy){
origenx=origenx+desplazamientox;
origeny=origeny+desplazamientoy;
Implementamos el
mtodo mover
}
public void ampliar (int zoomin){
Implementamos el
mtodo ampliar
Implementamos el
mtodo reducir
Declaramos
la
clase
Tringulo que hereda de
Figura
140
color=formulario.obtenerColor();
haydatos=true;
}
Implementamos el
mtodo paint
Implementamos el
mtodo mover
Implementamos el
mtodo ampliar
Implementamos el
mtodo reducir
141
4.21 Ejercicio
Reutilice el la solucin de figuras geomtricas para dibujar una cara. El aspecto
de la cara se muestra a continuacin. Puede suponer que el contorno de la cara es un
crculo, que los ojos tambin son crculos y que la boca es un rectngulo muy estrecho.
142
Una solucin
Pensar en objetos es asociar a un objeto la tarea de dibujar la cara. Sera un
objeto compuesto de otros objetos; uno por cada componente de la cara. Por ejemplo:
143
144
145
146
147
149
150
Bibliografa
[Booch 94] Grady Booch, Object Oriented Analysis and Design Ed. Benjamin
Cummings Publishing, 1994
151