Sie sind auf Seite 1von 49

Curso de OO dirigido por

la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

4. La herencia, ms aumento de la ambigedad


ndice
4.

La herencia, ms aumento de la ambigedad........................................................103


Sobre el captulo....................................................................................................104
Motivaciones.....................................................................................................104
Objetivo.............................................................................................................104
Contenido..........................................................................................................104
4.1
La herencia....................................................................................................105
4.2
El polimorfismo.............................................................................................106
4.3
La herencia como taxonoma.........................................................................107
4.4
Curiosidades biolgicas de la herencia software...........................................108
4.5
La herencia y la evolucin (equivocada) del software..................................109
4.6
Delegar en vez de heredar.............................................................................112
4.7
El principio de sustitucin de Liskov............................................................114
4.8
La evolucin segura del software..................................................................115
4.9
El aporte del principio de sustitucin, la ambigedad...................................117
4.10 Condiciones del principio de sustitucin.......................................................118
4.11 Contraejemplo de la herencia. El cuadrado no es un rectngulo...................119
4.12 Las clases abstractas......................................................................................122
4.13 Las clases generales, una solucin alternativa, pero.................................124
4.14 Las clases particulares, beneficios y problema..............................................126
4.15 La ambigedad, solucin al problema de la diversidad................................127
4.16 La ambigedad es la clave, no la divisin.....................................................128
4.17 La herencia mltiple......................................................................................128
4.18 Aproximacin al patrn adaptador................................................................131
4.19 La herencia vista desde el cdigo, un ejemplo..............................................131
4.20 El polimorfismo en Java................................................................................136
4.21 Ejercicio.........................................................................................................143
Una solucin..........................................................................................................144
4.22 Otro ejercicio.................................................................................................147
La bsqueda de la plasticidad, un cambio del diseo............................................148
La solucin del rectngulo.....................................................................................151
Bibliografa............................................................................................................152

103

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

Curso de OO dirigido por


la introduccin de ambigedad

4.1

La herencia, ms aumento de la ambigedad

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

En la Figura 4. 1, la clase B comparte las propiedades de la clase A, pero


redefine a2 y m2. La clase C, tambin, comparte los atributos de la clase A, pero aade
las propiedades a4 y m4. Las subclases no pueden rechazar ninguna propiedad de sus
105

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

superclases. Se acostumbra a escribir slo lo nuevo y se omiten las propiedades sin


cambio. La relacin de herencia se establece desde las subclases hacia la superclase. Es
decir, B y C indican explcitamente que son subclases de A en cada una de sus
declaraciones. La palabra subclase slo denota que es una clase que hereda y la palabra
superclase slo denota que es una clase de la que se hereda. La superclase y la subclase
reciben tambin reciben otros nombres, por ejemplo: clase base y clase derivada
respectivamente.
Desde el punto de vista del cdigo, los objetos de la subclase B son tambin
objetos de la clase A, aunque dos de sus propiedades (a2, m2) tienen cualidades distintas
a los objetos de la clase A. De modo semejante, los objetos de la subclase C son tambin
objetos de la clase A.

4.2

El polimorfismo
El polimorfismo es una de las cualidades importantes del enfoque de objetos por

su aporte de ambigedad en el diseo. Est asociado con el mecanismo de herencia y


permite que la operacin definida por una misma cabecera (signatura) sea implementada
de maneras distintas.
Se denomina polimorfismo a la capacidad de una operacin para manifestar un
comportamiento diferente dependiendo del objeto que la ejecuta. Por ejemplo, la
operacin m2 es polimrfica porque su comportamiento depende de si la ejecuta
un objeto de la clase A o un objeto de la clase B.
Los clientes de una operacin polimrfica la invocan a travs del mismo
mensaje, por ejemplo v.m2, pero el comportamiento depende del objeto que exprese la
variable v. A continuacin se muestra el pseudocdigo y resultado de una operacin que
utiliza el servicio polimrfico m2 ofrecido por objetos de las clases A, B y C.
v es A

declaracin de la variable v

v O:A

se asigna a la variable v un objeto de la clase A

v:m2

mensaje al objeto asignado a v para que ejecute m2

Se ejecuta la operacin m2 de la clase A


106

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

v es A

declaracin de la variable v

v O:C

se asigna a la variable v un objeto de la clase C

v.m2

mensaje al objeto asignado a v para que ejecute m2

Se ejecuta la operacin m2 de la clase A porque la clase C lo utiliza sin


redefinicin.
v es A

declaracin de la variable v

v O:B

se asigna a la variable v un objeto de la clase B

v.m2

mensaje al objeto asignado a v para que ejecute m2

Se ejecuta la operacin m2 de la clase B porque la clase B ha redefinido m2.


v es A

declaracin de la variable v

v O:C

se asigna a la variable v un objeto de la clase C

v.m4

mensaje al objeto asignado a v para que ejecute m2

Equivocacin: La clase A carece del mtodo m2.


En principio, cualquier operacin puede ser polimrfica a travs del mecanismo
de herencia, salvo las operaciones de creacin de objetos que por su tarea especfica, no
pueden ser polimrficas. Un constructor crea objetos de una clase y no de otra. Ms
adelante se volver a tratar este tema.

4.3

La herencia como taxonoma


Desde el punto de vista conceptual se podra decir que los objetos de B y C son

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

software se introducen fuentes de cambio. Segundo, porque las clasificaciones usuales


pueden ser contraproducentes para el sistema software como sucede con el clsico
ejemplo del cuadrado y el rectngulo, discutido ms adelante.

4.4

Curiosidades biolgicas de la herencia software


El sabor biolgico del mecanismo software denominado herencia se hace

explcito en su propio nombre. Algunos lenguajes se diferencian y le llaman extensin


(extends) o implementacin (implements) segn sea el caso. Hay varias
curiosidades asociadas con este mecanismo.
La herencia software parece un mecanismo sexuado puesto que un
descendiente puede tener varios progenitores. Vista as, la herencia simple se
corresponde con el fenmeno biolgico denominado partenognesis donde un solo
progenitor es capaz de crear. Los descendientes tienen, en general, el mismo sexo que el
progenitor. En la prctica software es una curiosidad sin trascendencia, aunque de valor
nemotcnico como se ver despus.
Otra curiosidad, pero esta vez clave, de la herencia software es que los
progenitores software desconocen a sus retoos, al revs de la mayora de los
humanos. En el enfoque de objetos, las hijas son las que reconocen a sus madres. La
nueva clase se declara hija de alguna clase ya existente como sucede con el pjaro cuco
insertado en nido ajeno. En otros casos los recin nacidos reconocen como progenitor a
cualquiera que est presente en el momento de su nacimiento. Si las madres software
tuviesen que declarar a las hijas, habra que modificar la declaracin (cdigo) de la
madre cada vez que se creara una hija. Por este motivo, la referencia hereditaria es de
descendientes hacia ascendientes, en el software de objetos.
La herencia software es un reflejo extremo del modelo de Lamarck donde se
heredan los cambios somticos. En la herencia software, si al progenitor (superclase)
se le quita o cambia una propiedad todos sus descendientes pierden o cambian,
inmediatamente, esa propiedad. Lamarck era contemporneo de Darwin, pero de ideas
diferentes. Esta otra curiosidad tambin es clave en el software. Si la madre software
adelgaza (se le quita alguna propiedad) todas las descendientes (hijas, nietas, choznas,
) nacidas o por nacer adelgazarn inmediatamente. En fin que, cualquier modificacin
108

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

en los predecesores se reflejar en los futuros sucesores y se propagar a los que ya


existen!, hecho que no sucede en ninguna herencia natural.
La propagacin de los cambios puede parecer una virtud de la herencia, pero
realmente es una catstrofe. En primer lugar porque cualquier clase puede ser madre de
muchos descendientes sin saberlo; no hay referencia a ellos. La bsqueda de los
descendientes es un problema laborioso, sobre todo si los esquemas no existen, estn
desactualizados o no se dispone de un medio automtico para hacerlo. En segundo
lugar, porque cualquier cambio afecta a quienes utilizan esa clase y localizar a los
afectados es una tarea mucho ms difcil de realizar. Mientras ms descendientes, mayor
ser el problema. La situacin es equivalente a una epidemia de cambios y fallos.

4.5

La herencia y la evolucin (equivocada) del software


El smil biolgico inspira a usar la herencia como una va de evolucin del

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Figura 4. 2 Ejemplo de herencia

En la figura slo aparecen los atributos y los mtodos aadidos o modificados


con respecto a los predecesores. Por ejemplo, la clase Hija 1 aade el atributo a4 y el
mtodo m4. La clase Hija 2 modifica el atributo a2 y el mtodo m2, y los declara para
diferenciarlos de los predecesores. La clase Nieta 1 aade los atributos a5 y a6, y el
mtodo m5. Pero tambin, modifica el mtodo m3 de la clase Madre, es decir de su
abuela. Y por ltimo, la clase Nieta 2 modifica el mtodo m2 de la clase Hija 2, su
madre. La descripcin ya es confusa.

110

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La facilidad que parece ofrecer la herencia para la evolucin del sistema


software se puede evaluar en la prctica con la Figura 4. 3, que se ha repetido en la
Figura 4. 2 por comodidad.

Figura 4. 3 Repeticin de la Figura 4. 2

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Resumiendo, la herencia es un potente instrumento de trabajo, pero peligroso


porque puede ocasionar graves efectos negativos segn se ha visto. La herencia ofrece
su mayor utilidad y menor riesgo cuando se emplea para ampliar la capacidad de
expresar ambigedad del diseo, segn se deriva del principio de Liskov. Para otro tipo
de uso es mejor delegar que heredar.

4.6

Delegar en vez de heredar


Esta idea es vieja en el software, ya tiene ms de quince aos. [Rumbaugh 95]

utiliz el ejemplo de una pila y una lista, pero se puede usar el ejemplo de cliente y
persona. Figura 4. 4

Figura 4. 4 Delegar en vez de heredar

Una situacin concreta puede sugerir el diseo de la derecha (herencia) porque


los clientes del negocio son personas. Los objetos de la clase Cliente son tambin de la
clase Persona en virtud de la relacin es un de la clase Cliente hacia la clase Persona.
Sin embargo, para ser cliente no es imprescindible ser persona por tanto la relacin
cliente es una persona resulta una relacin circunstancial susceptible de cambio.
Maana cliente puede ser una empresa o hasta un plutoniano, ahora que Plutn ha
dejado de ser un planeta (el eterno problema de las clasificaciones).

112

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

No conviene utilizar la herencia para relacionar Cliente y Persona por varias


razones. Primero, porque es previsible que cambie y cambiar, segn Murphy. Segundo,
porque los cambios son ms engorrosos dadas las fuertes ligaduras entre Cliente y
Persona a causa de las propiedades que hereda Cliente de Persona. Tercero, porque
conceptualmente es una relacin temporal y la herencia expresa una relacin
permanente.
La relacin de composicin de entre las clases Cliente y Persona (una parte de
persona es la situacin de cliente) reduce los inconvenientes citados porque una persona
siempre puede estar en la situacin de cliente, al menos de forma potencial. Por tanto, es
una relacin ms estable, ms prxima a la condicin de permanencia que expresa la
composicin, en consecuencia, hay menos posibilidad de cambio en la relacin.
Adems, de producirse cambios, seran menos engorrosos porque la ligadura de Cliente
hacia Persona es mucho ms dbil puesto que no hereda nada.
En vez de composicin se pudiera usar una relacin de asociacin, pero la
composicin enfatiza la pertenencia exclusiva del objeto cliente al objeto persona que lo
contiene. De este modo se debe asegurar que el objeto cliente de un objeto persona no
se comparta con otro objeto del sistema. Los lenguajes actuales no suministran esta
cualidad, si se quiere hay que implementarla.

113

Curso de OO dirigido por


la introduccin de ambigedad

4.7

La herencia, ms aumento de la ambigedad

El principio de sustitucin de Liskov


Despus del anlisis de los muchos problemas de la herencia, el principio de

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.

Figura 4. 5 Ejemplo de aplicacin del principio de sustitucin de Liskov

114

Curso de OO dirigido por


la introduccin de ambigedad

4.8

La herencia, ms aumento de la ambigedad

La evolucin segura del software


Pero lo interesante es ver el principio de sustitucin desde otra perspectiva.
La herencia permite sustituir un objeto por otro, es decir cambiar una tarea
por otra, sin riesgo, siempre que las subclases sean subtipos de las
superclases.
Si tenemos una clase S que hereda de la clase T, para usar un objeto de la clase S

dondequiera que se espere un objeto de la clase T, y que el sistema siga funcionando


correctamente, la condicin es que S debe ser subtipo de T. Si S no es subtipo de T no se
puede asegurar cul ser la consecuencia de la sustitucin.
La relacin de subtipo a tipo admite disear un tipo y despus especializarlo sin
alterar los vnculos del sistema con el tipo. Se facilita la evolucin del sistema. Por
ejemplo, primero disear un tipo A pensando en una tarea general y despus, inventar un
subtipo B que particularice o especialice esa tarea. O al revs, disear un tipo y ms
tarde convertirlo en un subtipo de otro tipo. La relacin de subtipo a tipo facilita un
antes y un despus sin daos colaterales. Figura 4. 6

Figura 4. 6 Evolucin del software

115

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La Figura 4. 6 ilustra una lnea de evolucin de un sistema software.


Inicialmente, la clase P est asociada con la clase A (el atributo v es de la clase A) y sus
objetos utilizan el servicio m2, a travs del mensaje v.m2, dirigido a objetos de A.
Despus, se quiere que m2 haga otra tarea, relacionada con la anterior pero distinta y se
inventa la clase B que redefine m2. Los objetos de la clase P pueden utilizar este nuevo
servicio dirigiendo el mismo mensaje v.m2 a los objetos de B.
Si la clase B es subtipo de la clase A, entonces es segura la sustitucin de los
objetos de la clase A por objetos de la clase B. El nico reajuste que requiere la
operacin cliente Op es cambiar la asignacin v O:A por v O:B.
El comportamiento interno de un programa no cambia cuando se sustituye un
objeto de un tipo por un objeto del subtipo. Al programa, por ejemplo a los clientes
como P, le es indiferente uno u otro objeto, mantiene una relacin ambigua con los
objetos de tipos y subtipos. Por tanto, se puede modificar la tarea, la funcin del
programa, sin alterar su trabajo. Una vez ms se consolida la direccin de la
ambigedad como lnea de diseo software para conseguir acomodo a los cambios, para
conseguir plasticidad. La Figura 4. 7 ilustra la relacin de ambigedad (indiferencia) de
P hacia A.

Figura 4. 7 Relacin de ambigedad


116

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

En la figura AAA, la relacin de P con el servicio m2 es ambigua porque se


establece a travs del mensaje v.m2, que no le importa si va dirigido a objetos de A, de B
o de E. La clase P est ligada a la clase A, pero puede usar los servicios de todos los
subtipos de A, indiferentemente. Se podra decir que P slo ve a A, los subtipos quedan
ocultos detrs del tipo. Los subtipos son los detalles que omite la abstraccin A.

4.9

El aporte del principio de sustitucin, la ambigedad


El aporte del principio de sustitucin es sealar el lado bueno de la herencia y

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.

4.10 Condiciones del principio de sustitucin


El cumplimiento del principio de sustitucin exige que los mtodos de las clases
derivadas deban mantener las siguientes relaciones con los mtodos de la clase base:
1. La clase derivada debe tener un mtodo correspondiente a cada mtodo de la
clase base. Este mtodo puede heredarse directamente de la clase base o
sobrescribirse.
2. Cada mtodo de la clase derivada que se corresponda a un mtodo de la clase
base debe requerir lo mismo o menos que la clase base. Es decir, si se
sobrescribe un mtodo heredado de la clase base, las precondiciones del
mtodo deben ser ms dbiles o permisivas que las del mtodo de la clase
base. Dicho de otro modo, si se sobrescribe en una clase derivada un mtodo
heredado de la clase base se debe garantizar que el nuevo mtodo funcione
en las mismas condiciones y recibiendo los mismos argumentos que el
117

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

mtodo heredado. El nuevo mtodo no puede ser ms restrictivo que el


mtodo heredado.
3. Cada mtodo de la clase derivada que se corresponda a un mtodo de la clase
base debe garantizar lo mismo o ms que la clase base. Es decir, si se
sobrescribe un mtodo heredado de la clase base, las postcondiciones del
mtodo de las clases derivada deben ser ms fuertes o rigurosas que las
heredadas de la clase base. Dicho de otro modo, el mtodo de la clase
derivada no debe comprometerse a ofrecer mayores resultados o resultados
diferentes; slo debe comprometerse a hacer lo que hace el mtodo de la
clase base, garantizando tambin las propiedades adicionales. Por ejemplo, si
un mtodo de la clase base devuelve un nmero mayor que el argumento que
recibe, un mtodo de una clase derivada podra devolver un nmero primo
mayor que el argumento. Pero no estara permitido que el mtodo de la clase
derivada devolviese un nmero menor o igual que el argumento.
4. Est permitido que la clase derivada introduzca nuevos mtodos adicionales
que no aparezcan en la clase base.
Las clases derivadas deben garantizar tambin que se cumplan todas las
restricciones definidas sobre los atributos que hereda de la clase base. Por ejemplo, si en
la clase base se define un atributo de tipo entero y se establece una restriccin para que
el atributo sea mayor o igual que cero, la clase derivada debe garantizar que se cumple
esta restriccin y que el atributo en la clase derivada tambin tendr siempre un valor
mayor o igual que cero.
Como podemos ver, cumplir con el principio de sustitucin de Liskov es fcil,
siempre que no modifiquemos ni sobrescribamos los atributos y mtodos que las clases
hijas heredan de sus madres y nos limitemos slo a aadir nuevos atributos y mtodos
adicionales en las clases hijas.
Si necesitamos modificar en las clases hijas los comportamientos heredados de
sus madres, entonces cumplir con el principio de Liskov puede resultar ms complicado.
Puede incluso, que al tratar de cumplir con el principio, debamos plantearnos si
realmente la clase hija debe heredar de la clase madre.

118

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

4.11 Contraejemplo de la herencia. El cuadrado no es un


rectngulo
Un ejemplo clsico de uso peligroso de la herencia es la relacin entre cuadrado
y rectngulo, analizada por Barbara Liskov en su trabajo sobre el principio.
Desde el colegio, todos sabemos que un rectngulo es una figura geomtrica de
cuatro lados iguales dos a dos. Y que un cuadrado es un rectngulo con todos los
lados iguales.
La herencia se suele considerar como una relacin es un, por tanto podra
parecernos natural que la clase Cuadrado heredase de la clase Rectngulo, ya que, al fin
y al cabo un cuadrado es un caso particular de rectngulo.
La clase Rectngulo bsicamente podra tener un par de atributos, ancho y largo,
que son suficientes para definir el rectngulo. Nuestra clase tendra un mtodo
establecerTamao que recibe como argumentos el ancho y el largo del rectngulo que
queremos definir.

Figura 4. 8. Clase Rectngulo

Si la clase Cuadrado hereda de la clase Rectngulo, entonces Cuadrado tendr


dos atributos, largo y ancho, y un mtodo establecerTamao que recibe como
argumentos el largo y el ancho del cuadrado. Ni los atributos ni los mtodos heredados
de Rectngulo resultan muy tiles para la clase Cuadrado, ya que todos sabemos que en
un cuadrado el largo y el ancho son siempre iguales. Para poder utilizar los atributos y
el mtodo heredado tendremos que aadir algunas restricciones:

119

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

1. el atributo largo tendr siempre el mismo valor que el atributo ancho.


2. el mtodo establecerTamao tendr como precondicin que el valor del
argumento ancho sea igual al valor del argumento largo. En caso contrario se
lanzar una excepcin o un mensaje de error.

Figura 4. 9 Relacin de herencia entre cuadrado y rectngulo

Preguntmonos ahora que ocurrira si sustituimos la clase Rectngulo por la


clase Cuadrado en algn lugar del cdigo donde se use Rectngulo. Podra un cliente
de Rectngulo trabajar con Cuadrado y seguir funcionando? La respuesta es no. A
menos que el cliente conozca y cumpla las restricciones de Cuadrado, si tratamos de
sustituir Cuadrado por Rectngulo el resultado ser impredecible.
Qu es lo que est ocurriendo en trminos del principio de sustitucin de
Liskov? El principio no se cumple porque los atributos y mtodos de Cuadrado son ms
restrictivos que los de Rectngulo. Cuadrado no es subtipo de Rectngulo. Por tanto, si
no queremos tener resultados inesperados no deberamos utilizar la herencia de
Cuadrado a Rectngulo. Herencia que, por otra parte, no aporta ninguna ventaja al
diseo, ya que Cuadrado necesita sobrescribir todo lo que hereda de Rectngulo para
funcionar.
120

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Nuestra intuicin nos ha llevado a pensar que Cuadrado debera heredar de


Rectngulo porque conceptualmente un cuadrado es un rectngulo. Sin embargo, hemos
visto que esta clasificacin, a pesar de ser cierta, no aporta ninguna ventaja a nuestro
diseo y s puede traernos serios inconvenientes. La herencia puede ser contradictoria
con la clasificacin usual. En los diseos orientados a objetos, copiar la realidad o
dejarse arrastrar por ella, no siempre es una buena idea. Un cuadrado es un rectngulo
en geometra, pero la clase Cuadrado no es un subtipo de la clase Rectngulo en el
software.

4.12 Las clases abstractas


El ejemplo del cuadrado y el rectngulo demuestra que la relacin geomtrica de
tipo y subtipo entre rectngulo y cuadrado no es aplicable a una relacin software entre
la clase Rectngulo y la clase Cuadrado. El universo software y el universo ajeno al
software son universos distintos.
En el universo software, si se quiere conseguir el intercambio seguro de objetos
cuadrados y objetos rectngulos, hay que aprovechar la relacin tipo subtipo, pero de
otra forma: definiendo a las clases Cuadrado y Rectngulo como subtipos (hermanos)
de un tipo que se podra denominar Figura. En la Figura 4. 10 se muestra esta relacin,
ampliada con la clase Paralelogramo (el supertipo geomtrico de rectngulo y
cuadrado, aqu obligado a ser un subtipo ms).

121

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Figura 4. 10 Una relacin prudente entre las clases Cuadrado, Rectngulo y


Paralelogramo

La relacin de la Figura 4. 10 permite el uso intercambiable de objetos cuadrado,


rectngulo y paralelogramo porque todos son subtipos de un tipo. La dificultad de la
relacin es la definicin del cdigo de los mtodos del tipo Figura. Pero el enfoque de
objetos salva esta dificultad, dando otro paso ms en su capacidad de expresar
ambigedad: permite que los mtodos del tipo sean slo nombres, nada de cdigo
interior. Es decir, que sean una abstraccin de los mtodos homnimos de los subtipos.
Este hecho da lugar a las siguientes definiciones:
Se denomina mtodo abstracto al mtodo que slo expresa la cabecera y carece
de cdigo interno. Dicho de otro modo, un mtodo que est declarado pero no
implementado [Booch 94]. Por ejemplo, los mtodos pintar, mover, ampliar y
rea, de la clase Figura. Tambin se les llama mtodos virtuales o diferidos. En
UML los mtodos abstractos se escriben con letra cursiva. En el presente texto,
la letra cursiva tiene el objetivo de resaltar, no indica abstraccin.
Se denomina clase abstracta a la clase que contiene al menos un mtodo
abstracto porque refleja la abstraccin de ese mtodo. Por ejemplo, la clase
Figura. Tambin se les llama clases virtuales o diferidas. Mientras que una clase
es la definicin de un conjunto de objetos, una clase abstracta es la definicin de
122

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

un conjunto de clases. Es una abstraccin de abstracciones que eleva la


capacidad de expresar ambigedad del enfoque estructurado. La carencia de
cdigo, en al menos un mtodo, impide que existan objetos de una clase
abstracta. En UML las clases abstractas se escriben con letra cursiva.
En UML se denomina interfaz a una coleccin de operaciones que tiene un
nombre y que se usa para especificar un servicio de una clase. Es un paso ms
elevado en el camino de la ambigedad en forma de abstraccin. Una interfaz
carece de atributos. UML realza la importancia de este tipo de abstraccin al
darle un nombre propio, pero la idea es anterior y coincide con la idea de clase
abstracta pura que utilizan otros autores. El problema de usar el nombre de
interfaz es que aumenta la polisemia de esta palabra. Frecuentemente se utiliza
la palabra inglesa interface como si fuese espaola. Pero la traduccin de la
palabra inglesa es interfaz, puesto que face significa cara, faz. Por cierto,
como la palabra faz es femenina, la palabra interfaz tambin es femenina y debe
ser precedida por el artculo la.
La clase Figura contiene solo el atributo color; prescinde de otros atributos que
pueden ser comprometedores (restrictivos), por ejemplo, p (posicin) y largo. Si se
coloca el atributo largo, la clase Figura se compromete slo con figuras geomtricas
que tengan algo recto, para que se pueda definir largo. El atributo p (posicin) parece
ms universal porque cualquier figura geomtrica en la pantalla debe tener una posicin.
Es cierto, pero el punto de la figura que determina la posicin puede tomar distintos
nombres, por ejemplo vrtice superior izquierdo en los paralelogramos y similares, y
centro en los crculos. Para facilitar que la clase Figura sea el tipo de todas las clases
de figuras conviene prescindir de atributos restrictivos, conviene elevar su capacidad de
expresar ambigedad.

4.13 Las clases generales, una solucin alternativa, pero


Una solucin alternativa al problema de los cuadrados y rectngulos es
aprovechar la clase Rectngulo para que tambin opere con cuadrados, pero como
rectngulos, sin distinguirlos. La clase Rectngulo no diferencia entre cuadrados y
rectngulos, pero los humanos podran hacerlo. Con una sola clase se ha resuelto el
problema de los cuadrados y los rectngulos.
123

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La solucin anterior se puede extender a la clase Paralelogramo y ahorrarse la


clase Rectngulo. Ahora, una sola clase sirve para resolver tres problemas: el cuadrado,
el rectngulo y el paralelogramo, que son casos particulares unos de otros desde el
punto de vista de la geometra. Figura 4. 11

Figura 4. 11 Generalizacin de una clase

Un paso ms all conduce a la clase polgono irregular (por qu limitarse al


regular) que se consigue con reajustes de atributos y mtodos (cada vez ms generales).
Y continuando con la tentacin (para qu tantas clases), se podra disponer de una nica
clase Figura* (para distinguirla de la otra) capaz de operar con cualquier figura
geomtrica. La Figura 4. 11 ilustra un posible proceso de generalizacin que conduce a
la clase Figura*. Se ha cambiado la disposicin usual para realzar el aumento de los
atributos a medida que la clase aumenta en generalidad. La confusin que produce la
figura es un efecto colateral, que tambin se produce cuando se trabaja con clases
generales.

124

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

misma piedra. Los humanos fueron especializando los distintos usos de la piedra en
instrumentos particulares. Tambin conviene hacerlo en el software.

4.14 Las clases particulares, beneficios y problema


Los instrumentos particulares slo exigen la informacin especfica que
necesitan; facilitan el desarrollo porque se pueden construir y probar uno a uno, o al
unsono distribuyendo el trabajo de desarrollo de los distintos instrumentos; se puede
modificar cualquiera de ellos sin afectar a los restantes. Incluso, se puede llegar a
construir un instrumento muy general, como Figura*, para resolver situaciones muy
generales, pero como un instrumento ms de uso especfico en esas situaciones. Sin
embargo, a veces todas estas ventajas quedan opacadas por el problema de la
diversidad. Sucede con frecuencia en el software.

4.15 La ambigedad, solucin al problema de la diversidad


El enfoque de objetos facilita la resolucin del problema de la diversidad a
travs de sus recursos para expresar y manejar ambigedad. La clase Figura es una
abstraccin que oculta u omite la diversidad (las clases) pero que viabiliza el acceso a
esa diversidad. Figura 4. 12

Figura 4. 12 Contraste entre soluciones

126

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La Figura 4. 12 contrasta del diseo de Figura y sus subtipos con el diseo de la


clase Figura*. Ambos diseos muestran una cara uniforme a sus elementos clientes. El
primero por ser una abstraccin y el segundo por ser una implementacin de una
herramienta general. La cara de la abstraccin es simple porque no se compromete con
los detalles, muestra slo la esencia. La cara de Figura* es compleja porque tiene que
expresar toda la informacin que necesita para resolver un problema complejo: ocuparse
de (casi) cualquier figura. La clase Figura es el contexto que permite dirigir la tarea al
objeto de clase encargada de una figura especfica. El objeto de la clase Figura* es el
encargado de realizar la tarea, cualquiera que sea la figura. La clase Figura es una
abstraccin, mientras que la clase Figura* es un elemento concreto, particular, aunque
implemente un mecanismo general.

4.16 La ambigedad es la clave, no la divisin


El contraste de ambos diseos puede sugerir que los beneficios de Figura y sus
subtipos deriva de aplicar el principio de divide y vencers o de su versin software:
modularizar. Pero no es as, ni tampoco est asociado con cohesin y acoplamiento. La
simplificacin se obtiene mediante la especificidad y la introduccin de ambigedad.
La clase Figura* es un mdulo cohesivo con bajo acoplamiento. Es un mdulo
bien diseado. Su divisin en partes ms pequeas no dara lugar a los subtipos porque
la divisin de un mecanismo general en trozos no produce mecanismos particulares.
Incluso, la propia clase Figura* podra ser un subtipo ms de Figura. Coexistira el
supuesto todo y las partes al mismo tiempo.
La fuente de los subtipos es la especificidad, lo individual y particular de cada
figura, que existe posiblemente antes de lo general de cualquier figura. El cuadrado, el
crculo y el tringulo existieron como concepto antes que el concepto general de figura.
La fuente de la clase Figura es la abstraccin, la expresin de lo comn a todas
las figuras del sistema software. La introduccin de este elemento ambiguo es la
decisin de diseo que permite homogeneizar la diversidad, disponer de un acceso
comn a todas las clases particulares.
La ambigedad que expresa la abstraccin simplifica la complejidad descriptiva
porque reduce la cantidad de elementos que se necesitan para describir el diseo. La
127

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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.

4.17 La herencia mltiple


Hasta ahora se ha estudiado la herencia simple de un solo progenitor
(partenogentica), pero tambin existe la herencia de varios progenitores.
Se denomina herencia mltiple a la herencia donde una subclase tiene ms de
una superclase directa. La Figura 4. 13 ilustra la herencia mltiple.

Figura 4. 13 Herencia mltiple

Haciendo un guio a la biologa, la herencia es como una reproduccin sexuada


donde cada progenitor aporta sus cualidades a los hijos. En el caso del modelo orientado
a objetos los hijos heredan el contenido completo sus progenitores. Por ejemplo,
refirindonos a la Figura 4. 13, la clase C hereda todos los atributos y mtodos de la
clase A y, tambin, todos los atributos y mtodos de la clase B.
Algunos textos utilizan este mecanismo para obtener elementos software
hbridos. Otros, como recurso de clasificacin mixta. Pero, si la herencia simple es
128

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

peligrosa, la herencia mltiple es mltiplemente peligrosa. Por ejemplo, a menudo se


ilustra la herencia mltiple a travs de las superclases Barco y Automvil para obtener la
clase Anfibio. Un problema es la distorsin del sujeto que se pretende representar
porque el anfibio es barco cuando est en el agua y automvil cuando est en tierra,
mientras que la clase Anfibio es Barco y Automvil a la vez porque la herencia es un
mecanismo esttico (en tiempo de compilacin).
Si el mecanismo hereditario fuese dinmico, en tiempo de ejecucin, un objeto
pudiera ser de una clase ahora y de otra despus. No habra confusiones porque no sera
de dos clases en el mismo momento. Aunque los lenguajes comerciales de
programacin orientada a objetos todava no incorporan una herencia dinmica, es
posible conseguir efectos semejantes con algunas tcnicas, referidas por [Fowler 97].
Otro problema es solapamiento del contenido gentico heredado. Por ejemplo,
la Figura 4. 14 muestra el resultado de la herencia mltiple de las clases Gallina y
Tiburn para conseguir una clase con propiedades de correr y nadar.

Figura 4. 14 Herencia mltiple con problema.

La dificultad de la clase Gallirn est en el atributo boca porque tiene dos


especificaciones distintas. Una manera de resolver el solapamiento gentico es con la
herencia dinmica. Otra, sera poder marcar la propiedad que se desea heredar, pero
no est permitido en los lenguajes comerciales actuales. Una tercera manera de evitar el
solapamiento es evitar que las superclases aporten elementos en posible conflicto.
129

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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).

4.18 Aproximacin al patrn adaptador


La doble cara de una clase facilita adaptar sus operaciones a la cara que
necesitan otras clases para usar esas operaciones. El denominado patrn adaptador
aprovecha el efecto de la doble cara que otorga la herencia mltiple. Figura 4. 15

Figura 4. 15 Variante del patrn adaptador

En la Figura 4. 15, la clase A contiene un mtodo n que se quiere incluir como


una implementacin ms del mtodo i, declarado en la clase abstracta pura B. Entonces,
se disea una clase C que herede de las clases A y B el mtodo n y la declaracin del
mtodo i respectivamente. Como ambos elementos se han juntado en la clase C, basta
con definir que la implementacin del mtodo i consiste en invocar al mtodo n para
conseguir que n sea una i ms. Esta ltima tcnica se denomina envolver.
130

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

4.19 La herencia vista desde el cdigo, un ejemplo


En captulos anteriores se disearon pequeos sistemas para trabajar con
tringulos, crculos y rectngulos. Ahora se quiere integrar estos sistemas en uno solo. A
continuacin se muestran los diseos de cada clase.

Figura 4. 16 Clase Crculo

Figura 4. 17 Clase Tringulo

131

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Figura 4. 18 Clase Rectngulo

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Figura 4. 19 Clase abstracta Figura

En la nueva clase Figura se ha incluido el mtodo concreto borrar() porque, de


inicio, se considera que ser comn a todos los subtipos, independientemente de la
figura. Una figura se borra pidindole que se pinte con el color de fondo. La
implementacin de borrar() estar en la clase madre y las hijas heredarn el cdigo de
ella. La presencia de este mtodo hace que la clase Figura sea abstracta, pero no
abstracta pura o interfaz. Adems, en esta clase se han aadido los atributos color y
haydatos que son comunes a todas las figuras.
En el diagrama el nombre de la clase Figura est escrito en letra cursiva porque
UML usa este tipo de letra para indicar que un elemento es abstracto. Tambin est
escritos con la misma letra los mtodos abstractos o virtuales paint(), mover(),
ampliar() y reducir().
La Figura 4. 20 muestra el diseo del diagrama de clases del sistema.

Figura 4. 20 Diagrama de clases de Figura polimrfica

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

4.20 El polimorfismo en Java


Veamos cmo se implementa el polimorfismo en el lenguaje Java. A
continuacin mostramos el cdigo de las clases Ventana, Figura, Crculo, Tringulo y
Rectngulo del ejemplo anterior. El cdigo del programa completo puede verse en el
anexo.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ventana extends JFrame implements ActionListener{
Declaramos en ventana el
atributo figura de la clase
Figura

private Figura figura;


private JPanel paneloperaciones, panelfiguras;

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

//Crear los paneles de botones de figuras y operaciones


panelfiguras = new JPanel();
panelfiguras.setLayout(new GridLayout());
paneloperaciones = new JPanel();
paneloperaciones.setLayout(new GridLayout());
//Crear los botones de figuras y aadirlos al panel de figuras
circulo=new JButton("Pintar Circulo");
circulo.addActionListener(this);
panelfiguras.add(circulo);
rectangulo=new JButton("Pintar Rectangulo");
rectangulo.addActionListener(this);
panelfiguras.add(rectangulo);
triangulo=new JButton("Pintar Triangulo");
triangulo.addActionListener(this);
panelfiguras.add(triangulo);
//Crear los botones de operaciones y aadirlos al panel de operaciones
//Tienen que estar inhabilitados hasta que se haya elegido una figura
ampliar=new JButton("Ampliar");
ampliar.addActionListener(this);
ampliar.setEnabled(false);
paneloperaciones.add(ampliar);
reducir=new JButton("Reducir");
reducir.addActionListener(this);
reducir.setEnabled(false);
paneloperaciones.add(reducir);
arriba=new JButton("Mover arriba");
arriba.addActionListener(this);
arriba.setEnabled(false);
paneloperaciones.add(arriba);
abajo=new JButton("Mover abajo");
abajo.addActionListener(this);
abajo.setEnabled(false);
paneloperaciones.add(abajo);
izqda=new JButton("Mover izqda");
izqda.addActionListener(this);
izqda.setEnabled(false);
paneloperaciones.add(izqda);
dcha=new JButton("Mover dcha");
dcha.addActionListener(this);
dcha.setEnabled(false);
paneloperaciones.add(dcha);
//Aadir los paneles de botones: figuras en la parte superior y
//operaciones en la parte inferior de la ventana
getContentPane().add(panelfiguras,BorderLayout.NORTH);
getContentPane().add(paneloperaciones,BorderLayout.SOUTH);
}
/** Manejador de eventos para controlar los botones */
public void actionPerformed(ActionEvent e)
{
int zoom=2;
int desplazamiento=20;
Object boton=e.getSource();
if (boton == circulo){
figura = new Circulo();
ampliar.setEnabled(true);
reducir.setEnabled(true);
arriba.setEnabled(true);
abajo.setEnabled(true);
izqda.setEnabled(true);

136

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

public abstract class Figura extends JPanel{

clase

protected Color color;


protected boolean haydatos=false;
public Figura() {

El mtodo paint debera ser


abstracto, pero para poder utilizar
el API swing necesitamos que
est implementado en todas las
clases.

}
public void paint(Graphics g){

137

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

super.paint(g);
}

public abstract void mover (int desplazamientox, int desplazamientoy);


public abstract void ampliar (int zoomin);

Declaramos los mtodos


abstractos mover, ampliar y
reducir

public abstract void reducir (int zoomout);


public void borrar(){
//Pintarme del color del fondo de la ventana
color= this.getBackground();
repaint();
}
}

Declaramos
Crculo que
Figura

public class Circulo extends Figura{


//Coordenada x del centro
private int centrox;

la
clase
hereda de

//Coordenada y del centro


private int centroy;
//Radio
private int radio;
//Crea una nueva instancia de Circulo
public Circulo() {
// Mostrar el formulario para obtener los datos del circulo
FormularioCirculo formulario= new FormularioCirculo();
//JDialog dialog = new JDialog(this, "Introduzca los datos del circulo", true);
JDialog dialog =new JDialog();
dialog.setTitle("Introduzca los datos del circulo");
dialog.setModal(true);
dialog.setContentPane(formulario);
dialog.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);
dialog.pack();
dialog.show();
// Obtener los datos introducidos por el usuario
centrox=formulario.obtenerCentrox();
centroy=formulario.obtenerCentroy();
radio=formulario.obtenerRadio();
color=formulario.obtenerColor();
haydatos=true;
}
public void paint (Graphics g)
{
super.paint(g);
//Si el usuario ha introducido los datos pinta el circulo
if (haydatos){
g.setColor(color);
g.drawOval(centrox-radio, centroy-radio,2*radio,2*radio);
g.fillOval(centrox-radio, centroy-radio,2*radio,2*radio);
g.dispose();
}
}

138

Implementamos el
mtodo paint

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

public void mover (int desplazamientox, int desplazamientoy){


Implementamos el
mtodo mover

centrox=centrox+desplazamientox;
centroy= centroy+desplazamientoy;
}
}
public void ampliar (int zoomin){

Implementamos el
mtodo ampliar

if (zoomin > 0){


radio=radio*zoomin;
}
}

Implementamos el
mtodo reducir

public void reducir (int zoomout){


if (zoomout > 0){
radio=radio/zoomout;
}
}

Declaramos
la
clase
Rectngulo que hereda de
Figura

public class Rectangulo extends Figura{


//Coordenada x del vertice superior izquierdo
private int origenx;
//Coordenada y del vertice superior izquierdo
private int origeny;
//Base
private int base;
//Altura
private int altura;
//Crea una nueva instancia de Rectangulo
public Rectangulo() {

// Mostrar el formulario para obtener los datos del rectangulo


FormularioRectangulo formulario= new FormularioRectangulo();
JDialog dialog =new JDialog();
dialog.setTitle("Introduzca los datos del rectangulo");
dialog.setModal(true);
dialog.setContentPane(formulario);
dialog.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);
dialog.pack();
dialog.show();
// Obtener los datos introducidos por el usuario
origenx=formulario.obtenerOrigenx();
origeny=formulario.obtenerOrigeny();
base=formulario.obtenerBase();
altura=formulario.obtenerAltura();
color=formulario.obtenerColor();
haydatos=true;
}

Implementamos el
mtodo paint

public void paint(Graphics g) {


super.paint(g);
//Si el usuario ha introducido los datos pinta el rectangulo
if (haydatos){

139

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

if (zoomin > 0){


base= base * zoomin;
altura=altura*zoomin;
}
}
public void reducir (int zoomout){

Implementamos el
mtodo reducir

if (zoomout > 0){


base= base / zoomout;
altura=altura / zoomout;
}
}
}

Declaramos
la
clase
Tringulo que hereda de
Figura

public class Triangulo extends Figura{


//Coordenada x del vertice superior
private int verticesuperiorx;
//Coordenada y del vertice superior
private int verticesuperiory;
//Base
private int base;
//Altura
private int altura;
// Crea una nueva instancia de Triangulo
public Triangulo () {

// Mostrar el formulario para obtener los datos del triangulo


FormularioTriangulo formulario= new FormularioTriangulo();
JDialog dialog =new JDialog();
dialog.setTitle("Introduzca los datos del triangulo");
dialog.setModal(true);
dialog.setContentPane(formulario);
dialog.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);
dialog.pack();
dialog.show();
// Obtener los datos introducidos por el usuario
verticesuperiorx=formulario.obtenerVerticeSuperiorx();
verticesuperiory=formulario.obtenerVerticeSuperiory();
base=formulario.obtenerBase();
altura=formulario.obtenerAltura();

140

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

color=formulario.obtenerColor();
haydatos=true;
}
Implementamos el
mtodo paint

public void paint(Graphics g) {


int [] coordenadasx=getCoordenadasX();
int [] coordenadasy=getCoordenadasY();
super.paint(g);
//Si el usuario ha introducido los datos pinta el triangulo
if (haydatos){
g.setColor(color);
g.drawPolygon(coordenadasx, coordenadasy, 3);
g.fillPolygon(coordenadasx, coordenadasy, 3);
g.dispose();
}
}
private int [] getCoordenadasX(){
int [] coordenadasx = new int [3];
coordenadasx[0]=verticesuperiorx;
coordenadasx[1]=verticesuperiorx-(base/2);
coordenadasx[2]=verticesuperiorx+(base/2);
return coordenadasx;
}
private int [] getCoordenadasY(){
int [] coordenadasy= new int[3];
coordenadasy[0]=verticesuperiory;
coordenadasy[1]=verticesuperiory+altura;
coordenadasy[2]=verticesuperiory+altura;
return coordenadasy;
}
public void mover (int desplazamientox, int desplazamientoy){

Implementamos el
mtodo mover

verticesuperiorx = verticesuperiorx + desplazamientox;


verticesuperiory = verticesuperiory + desplazamientoy;
}
public void ampliar (int zoomin){

Implementamos el
mtodo ampliar

if (zoomin > 0){


base= base * zoomin;
altura=altura*zoomin;
}
}

Implementamos el
mtodo reducir

public void reducir (int zoomout){


if (zoomout > 0){
base = base / zoomout;
altura = altura / zoomout;
}
}

141

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

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:

contorno, es un objeto de la clase Crculo asociado con el


contorno de la cara.

ojoderecho y ojoizquierdo, son dos objetos de la clase


Crculo asociados con los ojos.

boca, es un objeto de la clase Rectngulo asociado con la


boca.
Adems conviene considerar como atributos del objeto Cara

centrox y centroy, son dos nmeros enteros para indicar


las coordenadas del centro de la cara. Servirn para situar la cara en la pantalla.

tamanyo, un nmero entero para indicar el tamao de la


cara.

color, un atributo de tipo Color (tipo predefinido en Java)


que indica el color de la cara.
La cara estar contenida en una ventana, igual que en las figuras geomtricas,

porque se est trabajando en un entorno de ventanas (windows). La Figura 4. 21 muestra


el diagrama de clases.

143

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Figura 4. 21 Diagrama de clases de Pintar Cara

Las relaciones de agregacin entre las clases Cara, Crculo y Rectngulo


expresan que los objetos de la clase Cara estn compuestos por objetos de las clases
Crculo y Rectngulo. Estas relaciones reflejan el tipo de los atributos contorno,
ojoderecho, ojoizquierdo y boca.
Veamos a continuacin el cdigo de la clase Cara escrito en el lenguaje Java
public class Cara extends JPanel{ //extendsJPanel es necesario para usar el APi swing
private Circulo ojoderecho, ojoizquierdo; //ojos
private Rectangulo boca; //boca
private Circulo contorno; //contorno
private int tamanyo=200; //tamao de la cara
private int centrox=500; //coordenada x del centro de la cara
private int centroy=350; //coordenada y del centro de la cara
private Color color=Color.YELLOW; //color de fondo de la cara
// Crea una nueva instancia de Cara
public Cara() {
//crear el contorno
contorno = new Circulo(centrox, centroy, tamanyo, color);
//crear los ojos
ojoizquierdo = new Circulo(centrox-(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK );
ojoderecho = new Circulo(centrox+(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK );
//crear la boca
boca = new Rectangulo(centrox-(tamanyo/4), centroy+(tamanyo/2), tamanyo/2, 2, Color.BLACK);
}

144

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

public void paint (Graphics g)


{
super.paint(g); //necesario para utilizar el API swing
//pintar el contorno
contorno.paint(g);
//pintar los ojos
ojoderecho.paint(g);
ojoizquierdo.paint(g);
//pintar la boca
boca.paint(g);
}
}

La solucin pinta la cara reutilizando la estructura de figuras desarrollada


anteriormente. La Figura 4. 22 muestra el resultado de la ejecucin del programa.

Figura 4. 22 Resultado de Pintar Cara

145

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

4.22 Otro ejercicio


La cara que pintamos no les gust a los nios y propusieron que el contorno
fuese un cuadrado. Haga las modificaciones necesarias en el diseo para satisfacer la
nueva solicitud. De paso, evale la facilidad que ofrece su diseo anterior para ajustarse
a este cambio.

Una solucin posible, consiste en reutilizar el diseo anterior modificando el


atributo contorno de la clase Cara para que sea un objeto de la clase Rectngulo en
lugar de Crculo. Como ambas clases, Rectngulo y Crculo, tienen un mtodo llamado
paint(), no sera necesario modificar el mtodo paint() de Cara, ya que el mensaje
contorno.paint() sigue siendo vlido.

146

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La bsqueda de la plasticidad, un cambio del diseo


El cambio o reajuste de requisitos es un fenmeno muy frecuente en el software,
tanto que resulta ser uno de los problemas ms importantes (y tambin rentables) del
desarrollo de software.
La solucin anterior al ejercicio de las caras resolvi la situacin de modo
directo, sin pensar ms all, porque lo que no se sabe, no se sabe. De lo contrario en el
software se contrataran orculos, videntes, cualquiera que sea capaz de anticiparse (con
exactitud), pero no dan resultado. La solucin anterior, sin otro conocimiento, est bien.
Ahora han variado las condiciones y ya se sabe que se quiere modificar el
contorno. Pero, si hay cambios en el contorno podr haber cambios en cualquier otro
atributo de la cara. Entonces, conviene disear una solucin de mayor plasticidad
(deformable), que admita como componente de la cara cualquier figura. Se trata de
introducir ambigedad en el diseo.
Un paso en esa direccin es asignar los atributos contorno, ojoizquierdo,
ojoderecho y boca de tipo Figura, usando la palabra tipo en el sentido estricto de
Liskov. La intencin es aprovechar la posibilidad de polimorfismo que ofrecen las
operaciones de la clase Figura.
El diseo de los componentes de la cara como objetos de la clase Figura
introduce suficiente ambigedad para componer la cara con cualquier figura de las
disponibles actualmente o en el futuro.
Otro paso que aumenta la plasticidad del diseo es agrupar los componentes de
la cara en un vector (facciones) con la finalidad de facilitar la uniformidad del
tratamiento. El uso de un vector de componentes aade ambigedad al diseo porque
los nombres de los componentes se sustituyen por ordinales: el primer componente, el
segundo, etc. Gracias a esta ambigedad se gana en igualdad de proceso y en facilidad
de modificacin. Pero la ambigedad tambin puede dificultar la comprensin por un
efecto de desconcierto. Casi nada es gratis. Para reducir el posible efecto negativo de la
ambigedad se han utilizado variables temporales con los nombres de los componentes.

147

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Sin embargo, a pesar de toda la ambigedad introducida en el diseo, se


mantienen dependencias directas de Cara hacia Crculo y Rectngulo a causa de las
operaciones de creacin de objetos. Slo cuando los creamos necesitamos declarar su
tipo especfico Crculo y Rectngulo (recordemos que no pueden crease objetos de
clases abstractas como Figura). En la aproximacin a los patrones se ver una solucin
que reducen estas ligaduras.
Una vez creados los objetos podemos tratarlos a todos por igual usando los
mtodos de la clase Figura. Por eso ahora, en el mtodo paint() de Cara utilizamos un
objeto de tipo Figura para recorrer el vector facciones y pintar cada uno de sus
elementos. Como todos los elementos de facciones son de tipo Figura siempre
podremos llamar al mtodo paint() del elemento concreto utilizando la cabecera que
heredan todos de Figura.
La Figura 4. 23 muestra el diseo del diagrama de clases de esta otra nueva
solucin de ms plasticidad.

Figura 4. 23 Diagrama de clases de Pintar Cara polimrfico

El diseo de la Figura 4. 23 aumenta la facilidad de extensin y modificacin del


sistema (plasticidad) pero conserva la funcin del sistema; todava los contornos de las
caras son crculos. Este ejemplo ilustra la siguiente definicin:
148

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

Se denomina factorizacin a las modificaciones que se realizan en los diseos


para mejorar alguna cualidad interna del sistema sin variar sus funciones.
Veamos el cdigo Java de esta nueva clase Cara.

149

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La solucin del rectngulo


Dada la plasticidad del diseo nuevo, el cambio del contorno de la cara se
resuelve slo modificando la lnea en la que creamos el objeto contorno para crear un
Rectngulo en lugar de un Crculo. Veamos este cambio sobre el cdigo.

150

Curso de OO dirigido por


la introduccin de ambigedad

La herencia, ms aumento de la ambigedad

La Figura 4. 24 muestra el resultado de la ejecucin del programa.

Figura 4. 24 Resultado de Pintar Cara

Bibliografa
[Booch 94] Grady Booch, Object Oriented Analysis and Design Ed. Benjamin
Cummings Publishing, 1994

[Rumbaugh 95] James Rumbaugh et al. Modelado y Diseo Orientado a


Objetos Ed. Prentice Hall, 1995
[Liskov 86] Barbara Liskov et al. Abstraction and Specification in Program
Development Cambridge, MA: The MIT Press, 1988
[Fowler 97] Martin Fowler, Analysis Patterns Ed. Addison-Wesley, 1997

151

Das könnte Ihnen auch gefallen