Está en la página 1de 61

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

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.

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

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

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
v O:A
v:m2

declaracin de la variable v
se asigna a la variable v un objeto de la clase A
mensaje al objeto asignado a v para que ejecute m2

Se ejecuta la operacin m2 de la clase A


v es A
v O:C
v.m2

declaracin de la variable v
se asigna a la variable v un objeto de la clase C
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
v O:B
v.m2

declaracin de la variable v
se asigna a la variable v un objeto de la clase B
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
v O:C
v.m4

declaracin de la variable v
se asigna a la variable v un objeto de la clase C
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.

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

por

nacer

adelgazarn inmediatamente. En fin que, cualquier modificacin 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.

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.

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

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.

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

7 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

8 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

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

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.

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.

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

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

ancho,

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

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.

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

13 Las clases generales, una solucin alternativa, pero


Una

solucin

alternativa

al

problema

de

los

cuadrados

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

por

estas

dos

causas

es

ms

susceptible

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

misma

piedra.

Los

humanos

fueron

especializando

los

distintos

usos

de

la

piedra

en

instrumentos

particulares. Tambin conviene hacerlo en el software.

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.

15 La ambigedad, solucin al problema de la diversidad


El enfoque de objetos facilita la resolucin del problema de la
diversidad

travs

de

sus

recursos

para

expresar

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

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.

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

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

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.

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

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

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

Rectngulo.

Pero

como

todos

comparten

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.

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{
private Figura figura;
private JPanel paneloperaciones, panelfiguras;

Declaramos en ventana el
atributo figura de la clase
Figura

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(){
//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);
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);

Declaramos la clase abstracta Figura

El mtodo paint debera ser abstracto, pero para poder utilizar el API swing necesitamos que est implementado en todas las cl

public static void main(String[] args) {


new Ventana();
}

Declaramos los mtodos abstractos mover, ampliar y reducir

}
public abstract class Figura extends JPanel{
protected Color color;
protected boolean haydatos=false;

public Figura() {

Declaramos la clase Crculo que hereda de Figura

}
public void paint(Graphics g){
super.paint(g);
}

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


public abstract void ampliar (int zoomin);
public abstract void reducir (int zoomout);
public void borrar(){
//Pintarme del color del fondo de la ventana
color= this.getBackground();
repaint();
}
}
public class Circulo extends Figura{
//Coordenada x del centro
private int centrox;
//Coordenada y del centro
private int centroy;
//Radio
private int radio;

//Crea una nueva instancia de Circulo


public Circulo() {

Implementamos el mtodo paint

// Mostrar el formulario para obtener los datos del circulo


FormularioCirculo formulario= new FormularioCirculo();
Implementamos
el mtodo mover
//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();
Implementamos el mtodo ampliar
dialog.show();
// Obtener los datos introducidos por el usuario
centrox=formulario.obtenerCentrox();
centroy=formulario.obtenerCentroy();
radio=formulario.obtenerRadio();
color=formulario.obtenerColor();
haydatos=true;

Implementamos el mtodo reducir

}
public void paint (Graphics g)
Declaramos la clase Rectngulo que hereda de Figura
{
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();
}
}

public void mover (int desplazamientox, int desplazamientoy){


centrox=centrox+desplazamientox;
centroy= centroy+desplazamientoy;
}
}
public void ampliar (int zoomin){
if (zoomin > 0){
radio=radio*zoomin;
}
}
public void reducir (int zoomout){
if (zoomout > 0){
radio=radio/zoomout;
}
}
Implementamos el mtodo paint

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;

Implementamos el mtodo mover

//Altura
private int altura;
Implementamos el mtodo ampliar
//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);
Implementamos el mtodo reducir
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;

Declaramos la clase Tringulo que hereda de Figura

}
public void paint(Graphics g) {
super.paint(g);
//Si el usuario ha introducido los datos pinta el rectangulo
if (haydatos){
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;
}

public void ampliar (int zoomin){


if (zoomin > 0){
base= base * zoomin;
altura=altura*zoomin;
}

Implementamos el mtodo paint

}
public void reducir (int zoomout){
if (zoomout > 0){
base= base / zoomout;
altura=altura / zoomout;
}
}
}
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();
Implementamos el mtodo mover
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();
Implementamos el mtodo ampliar
dialog.show();
// Obtener los datos introducidos por el usuario
verticesuperiorx=formulario.obtenerVerticeSuperiorx();
verticesuperiory=formulario.obtenerVerticeSuperiory();
base=formulario.obtenerBase();
altura=formulario.obtenerAltura();
color=formulario.obtenerColor();
Implementamos el mtodo reducir
haydatos=true;
}

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){
verticesuperiorx = verticesuperiorx + desplazamientox;
verticesuperiory = verticesuperiory + desplazamientoy;
}
public void ampliar (int zoomin){
if (zoomin > 0){
base= base * zoomin;
altura=altura*zoomin;
}
}
public void reducir (int zoomout){

if (zoomout > 0){


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

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.

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.

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);
}

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

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.

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

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.

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. AddisonWesley, 1997

También podría gustarte