70
Curso de OO dirigido por La herencia, más aumento de la ambigüedad la introducción de ambigüedad 4. La herencia, más aumento de la ambigüedad Índice 4. La herencia, más aumento de la ambigüedad...........103 Sobre el capítulo...................................104 Motivaciones......................................104 Objetivo..........................................104 Contenido.........................................104 4.1 La herencia......................................105 4.2 El polimorfismo..................................106 4.3 La herencia como taxonomía.......................107 4.4 Curiosidades biológicas de la herencia software. .108 4.5 La herencia y la evolución (equivocada) del software 109 4.6 Delegar en vez de heredar........................112 4.7 El principio de sustitución de Liskov............114 4.8 La evolución segura del software.................115 4.9 El aporte del principio de sustitución, la ambigüedad............................................117 4.10 Condiciones del principio de sustitución.........118 4.11 Contraejemplo de la herencia. El cuadrado no es un rectángulo............................................119 4.12 Las clases abstractas............................122 4.13 Las clases generales, una solución alternativa, pero….................................................124 4.14 Las clases particulares, beneficios y problema. . .126 4.15 La ambigüedad, solución al problema de la diversidad 127 4.16 La ambigüedad es la clave, no la división........128 4.17 La herencia múltiple.............................128 4.18 Aproximación al patrón adaptador.................131 4.19 La herencia vista desde el código, un ejemplo....131 4.20 El polimorfismo en Java..........................136 4.21 Ejercicio........................................143 Una solución........................................ 144 4.22 Otro ejercicio...................................147 La búsqueda de la plasticidad, un cambio del diseño. 148 103

04CursoOO_herencia

Embed Size (px)

DESCRIPTION

Herencia programación OO

Citation preview

Page 1: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4. La herencia, más aumento de la ambigüedad

Índice

4. La herencia, más aumento de la ambigüedad........................................................103Sobre el capítulo....................................................................................................104

Motivaciones.....................................................................................................104Objetivo.............................................................................................................104Contenido..........................................................................................................104

4.1 La herencia....................................................................................................1054.2 El polimorfismo.............................................................................................1064.3 La herencia como taxonomía.........................................................................1074.4 Curiosidades biológicas de la herencia software...........................................1084.5 La herencia y la evolución (equivocada) del software..................................1094.6 Delegar en vez de heredar.............................................................................1124.7 El principio de sustitución de Liskov............................................................1144.8 La evolución segura del software..................................................................1154.9 El aporte del principio de sustitución, la ambigüedad...................................1174.10 Condiciones del principio de sustitución.......................................................1184.11 Contraejemplo de la herencia. El cuadrado no es un rectángulo...................1194.12 Las clases abstractas......................................................................................1224.13 Las clases generales, una solución alternativa, pero….................................1244.14 Las clases particulares, beneficios y problema..............................................1264.15 La ambigüedad, solución al problema de la diversidad................................1274.16 La ambigüedad es la clave, no la división.....................................................1284.17 La herencia múltiple......................................................................................1284.18 Aproximación al patrón adaptador................................................................1314.19 La herencia vista desde el código, un ejemplo..............................................1314.20 El polimorfismo en Java................................................................................1364.21 Ejercicio.........................................................................................................143

Una solución..........................................................................................................1444.22 Otro ejercicio.................................................................................................147

La búsqueda de la plasticidad, un cambio del diseño............................................148La solución del rectángulo.....................................................................................151Bibliografía............................................................................................................152

103

Page 2: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Sobre el capítulo

Motivaciones

La herencia ha sido y es un canto de sirena en los objetos. Quien lo escucha con oídos ingenuos se hunde con el sistema. Parece que la herencia es la fuente de ahorro de código, de la facilidad de modificación y extensión 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 ambigüedad y la forma adecuada de uso es mediante el polimorfismo.

Objetivo

El objetivo del presente capítulo 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 sustitución de Liskov, como forma segura y útil de la herencia

5. Las clases abstractas

6. Que la ambigüedad es la clave, no la división

Contenido

La primera parte profundiza en la herencia, sus aspectos buenos y malos, y recomienda que es preferible delegar que heredar. Se enuncia también el concepto de polimorfismo.

La segunda parte estudia con detalle el principio de sustitución de Liskov, sus condiciones y cómo un cuadrado no es un rectángulo, en términos de la herencia software.

La tercera parte se dedica a las clases abstractas; a la ambigüedad como solución de la diversidad y, en general, como solución más poderosa que el limitado principio de divide y vencerás.

La cuarta parte discute la herencia múltiple, sus efectos nefastos actuales y las restrictivas condiciones donde es favorable, por ejemplo, en el patrón adaptador.

104

Page 3: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.1 La herencia

La herencia es uno de los cantos de sirenas del enfoque de objetos. Se dice

mucho de sus favores y muy poco de sus peligros. Por esta causa primero se estudiará la

definición y los peligros, y después 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 relación entre clases donde una clase comparte la estructura o

comportamiento definido en otra clase (herencia simple) o en más clases

(herencia múltiple). La herencia define una jerarquía “es-un” entre clases, en la

cual una subclase hereda de una o más clases generalizadas; una subclase

típicamente especializa su superclase añadiendo 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, también, comparte los atributos de la clase A, pero añade

las propiedades a4 y m4. Las subclases no pueden rechazar ninguna propiedad de sus

105

A

a1a2a3

m1m2m3

B

a1a2a3

m1m2m3

C

a1a2a3a4

m1m2m3m4

herenciasuperclase

subclases

Page 4: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

cambio. La relación de herencia se establece desde las subclases hacia la superclase. Es

decir, B y C indican explícitamente que son subclases de A en cada una de sus

declaraciones. La palabra subclase sólo denota que es una clase que hereda y la palabra

superclase sólo denota que es una clase de la que se hereda. La superclase y la subclase

reciben también reciben otros nombres, por ejemplo: clase base y clase derivada

respectivamente.

Desde el punto de vista del código, los objetos de la subclase B son también

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 también

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 ambigüedad en el diseño. Está asociado con el mecanismo de herencia y

permite que la operación definida por una misma cabecera (signatura) sea implementada

de maneras distintas.

Se denomina polimorfismo a la capacidad de una operación para manifestar un

comportamiento diferente dependiendo del objeto que la ejecuta. Por ejemplo, la

operación m2 es polimórfica 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 operación polimórfica la invocan a través del mismo

mensaje, por ejemplo v.m2, pero el comportamiento depende del objeto que exprese la

variable v. A continuación se muestra el pseudocódigo y resultado de una operación que

utiliza el servicio polimórfico m2 ofrecido por objetos de las clases A, B y C.

v es A declaración 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 operación m2 de la clase A

106

Page 5: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

v es A declaración 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 operación m2 de la clase A porque la clase C lo utiliza sin

redefinición.

v es A declaración 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 operación m2 de la clase B porque la clase B ha redefinido m2.

v es A declaración 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

Equivocación: La clase A carece del método m2.

En principio, cualquier operación puede ser polimórfica a través del mecanismo

de herencia, salvo las operaciones de creación de objetos que por su tarea específica, no

pueden ser polimórficas. Un constructor crea objetos de una clase y no de otra. Más

adelante se volverá a tratar este tema.

4.3 La herencia como taxonomía

Desde el punto de vista conceptual se podría decir que los objetos de B y C son

variantes de A. Esta idea permite asociar la herencia con una relación jerárquica “es un”.

Es decir, como un instrumento de clasificación, al estilo taxonómico de las ciencias

naturales. Abundan los ejemplos que ilustran la herencia a través de modelos

taxonómicos: la clase perro es una subclase de la clase mamífero. Pero, en general,

constituye un ejemplo poco afortunado. Primero, porque cualquier clasificación es

subjetiva y siempre admite objeciones, de manera que al introducir clasificaciones en el

107

Page 6: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

pueden ser contraproducentes para el sistema software como sucede con el clásico

ejemplo del cuadrado y el rectángulo, discutido más adelante.

4.4 Curiosidades biológicas de la herencia software

El sabor biológico del mecanismo software denominado herencia se hace

explícito en su propio nombre. Algunos lenguajes se diferencian y le llaman extensión

(“extends”) o implementación (“implements”) según 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 fenómeno biológico denominado partenogénesis donde un solo

progenitor es capaz de crear. Los descendientes tienen, en general, el mismo sexo que el

progenitor. En la práctica software es una curiosidad sin trascendencia, aunque de valor

nemotécnico como se verá después.

Otra curiosidad, pero esta vez clave, de la “herencia” software es que los

“progenitores” software desconocen a sus retoños, al revés de la mayoría 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 pájaro cuco

insertado en nido ajeno. En otros casos los recién 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, habría que modificar la declaración (código) 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 somáticos. 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 contemporáneo de Darwin, pero de ideas

diferentes. Esta otra curiosidad también 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 adelgazarán inmediatamente. En fin que, cualquier modificación

108

Page 7: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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 propagación de los cambios puede parecer una virtud de la herencia, pero

realmente es una catástrofe. En primer lugar porque cualquier clase puede ser madre de

muchos descendientes sin saberlo; no hay referencia a ellos. La búsqueda de los

descendientes es un problema laborioso, sobre todo si los esquemas no existen, están

desactualizados o no se dispone de un medio automático para hacerlo. En segundo

lugar, porque cualquier cambio afecta a quienes utilizan esa clase y localizar a los

afectados es una tarea mucho más difícil de realizar. Mientras más descendientes, mayor

será el problema. La situación es equivalente a una epidemia de cambios y fallos.

4.5 La herencia y la evolución (equivocada) del software

El símil biológico inspira a usar la herencia como una vía de evolución del

software, que se ajusta a las nuevas necesidades, a través de la creación de clases hijas

que heredan las propiedades de las clases existente y las modifican o añaden otras

propiedades nuevas. Parece que la herencia permite ahorrar código, no tocar lo que ya

funciona, y además, permite que se puedan cambiar muchas clases cambiando una sola.

Son ideas atractivas, como las sirenas.

La Figura 4. 2 muestra un árbol jerárquico de herencia cuya raíz 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 métodos se han separado

por comas, aunque cada uno debe ir escrito en una línea, según la notación de UML.

109

Page 8: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Figura 4. 2 Ejemplo de herencia

En la figura sólo aparecen los atributos y los métodos añadidos o modificados

con respecto a los predecesores. Por ejemplo, la clase Hija 1 añade el atributo a4 y el

método m4. La clase Hija 2 modifica el atributo a2 y el método m2, y los declara para

diferenciarlos de los predecesores. La clase Nieta 1 añade los atributos a5 y a6, y el

método m5. Pero también, modifica el método m3 de la clase Madre, es decir de su

abuela. Y por último, la clase Nieta 2 modifica el método m2 de la clase Hija 2, su

madre. La descripción ya es confusa.

110

Madre

a1,a2,a3

m1,m2,m3

herencia

Hija2

a2

m2

Hija1

a4

m4

Nieta2

m2

Nieta1

a5, a6

m3, m5

soy hija de

soy hija de

Page 9: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La facilidad que parece ofrecer la herencia para la evolución del sistema

software se puede evaluar en la práctica con la Figura 4. 3, que se ha repetido en la

Figura 4. 2 por comodidad.

Figura 4. 3 Repetición de la Figura 4. 2

Al ver en el código la clase Nieta 2, se puede pensar que sólo 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,

según la madre, Hija 2. Pero, la historia continua. Hija 2 es hija de Madre. Entonces,

Nieta 2 no está casi vacía 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 también,

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

título de un delicioso enredo teatral de Jardiel Poncela. Mientras más genealogía, más

calvario y mayor probabilidad de equivocaciones. Por otra parte, si se quiere reutilizar

alguna clase hija habrá que llevarse a la nueva aplicación, todas las clases antecesoras.

111

Madre

a1,a2,a3

m1,m2,m3

herencia

Hija2

a2

m2

Hija1

a4

m4

Nieta2

m2

Nieta1

a5, a6

m3, m5

soy hija de

soy hija de

Page 10: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

porque puede ocasionar graves efectos negativos según se ha visto. La herencia ofrece

su mayor utilidad y menor riesgo cuando se emplea para ampliar la capacidad de

expresar ambigüedad del diseño, según 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 más de quince años. [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 situación concreta puede sugerir el diseño de la derecha (herencia) porque

los clientes del negocio son personas. Los objetos de la clase Cliente son también de la

clase Persona en virtud de la relación “es un” de la clase Cliente hacia la clase Persona.

Sin embargo, para ser cliente no es imprescindible ser persona por tanto la relación

“cliente es una persona” resulta una relación circunstancial susceptible de cambio.

Mañana cliente puede ser una empresa o hasta un plutoniano, ahora que Plutón ha

dejado de ser un planeta (el eterno problema de las clasificaciones).

112

Persona

Cliente

Persona

Cliente

Delegar en vez de heredar

Page 11: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

razones. Primero, porque es previsible que cambie y cambiará, según Murphy. Segundo,

porque los cambios son más engorrosos dadas las fuertes ligaduras entre Cliente y

Persona a causa de las propiedades que hereda Cliente de Persona. Tercero, porque

conceptualmente es una relación temporal y la herencia expresa una relación

permanente.

La relación de composición de entre las clases Cliente y Persona (una parte de

persona es la situación de cliente) reduce los inconvenientes citados porque una persona

siempre puede estar en la situación de cliente, al menos de forma potencial. Por tanto, es

una relación más estable, más próxima a la condición de permanencia que expresa la

composición, en consecuencia, hay menos posibilidad de cambio en la relación.

Además, de producirse cambios, serían menos engorrosos porque la ligadura de Cliente

hacia Persona es mucho más débil puesto que no hereda nada.

En vez de composición se pudiera usar una relación de asociación, pero la

composición 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

Page 12: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.7 El principio de sustitución de Liskov

Después del análisis de los muchos problemas de la herencia, el principio de

sustitución de Liskov, formulado hace casi dos décadas, ofrece un camino útil y

confiable para aprovechar los favores de la herencia.

Literalmente el principio define, en términos de una sustitución 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 términos 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 aplicación del principio. Cuando O1:S es sustituido por

O2:T ningún objeto, por ejemplo :N, que espera a O2:T se altera si recibe a O1:S.

Figura 4. 5 Ejemplo de aplicación del principio de sustitución de Liskov

114

:N O1:S

m()

T

S

m()

m()

S es subtipo de T sii O2:T es sustituido por O1:S, y :N no cambia

Principio de sustitución de Liskov

:N O2:T

m()

Page 13: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.8 La evolución segura del software

Pero lo interesante es ver el principio de sustitución 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 condición es que S debe ser subtipo de T. Si S no es subtipo de T no se

puede asegurar cuál será la consecuencia de la sustitución.

La relación de subtipo a tipo admite diseñar un tipo y después especializarlo sin

alterar los vínculos del sistema con el tipo. Se facilita la evolución del sistema. Por

ejemplo, primero diseñar un tipo A pensando en una tarea general y después, inventar un

subtipo B que particularice o especialice esa tarea. O al revés, diseñar un tipo y más

tarde convertirlo en un subtipo de otro tipo. La relación de subtipo a tipo facilita un

antes y un después sin daños colaterales. Figura 4. 6

Figura 4. 6 Evolución del software

115

P

Op {

v es A

A

a1

m2v ← O:Av.m2

}

B

m2

P

Op {

v es A

A

a1

m2

v ← O:Bv.m2

}

Antes

Después

Evolución

Page 14: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La Figura 4. 6 ilustra una línea de evolución 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 través del mensaje v.m2, dirigido a objetos de A.

Después, 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 sustitución de los

objetos de la clase A por objetos de la clase B. El único reajuste que requiere la

operación cliente Op es cambiar la asignación “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 relación ambigua con los

objetos de tipos y subtipos. Por tanto, se puede modificar la tarea, la función del

programa, sin alterar su trabajo. Una vez más se consolida la dirección de la

ambigüedad como línea de diseño software para conseguir acomodo a los cambios, para

conseguir plasticidad. La Figura 4. 7 ilustra la relación de ambigüedad (indiferencia) de

P hacia A.

Figura 4. 7 Relación de ambigüedad

116

B

a1a2a3

m1m2m3

E

a1a2a3

m1m2m3

…v ← O:?v.m2…}

P

Op {

v es A

A

a1a2a3

m1m2m3

Page 15: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

En la figura AAA, la relación de P con el servicio m2 es ambigua porque se

establece a través 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 podría decir que P sólo ve a A, los subtipos quedan

ocultos detrás del tipo. Los subtipos son los “detalles” que omite la abstracción A.

4.9 El aporte del principio de sustitución, la ambigüedad

El aporte del principio de sustitución es señalar el lado “bueno” de la herencia y

ofrecer una forma segura de usarlo. Pero, cuál es el lado “bueno” de la herencia porque

antes de estudiar el principio sólo se habían visto lados problemáticos. El propio título

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 ambigüedad en el diseño y así facilitar el manejo

de la complejidad descriptiva y de incertidumbre.

La evolución, expansión o modificación del software a través de la herencia

debe cumplir las condiciones del principio de sustitución de Liskov para evitar

dificultades.

4.10 Condiciones del principio de sustitución

El cumplimiento del principio de sustitución exige que los métodos de las clases

derivadas deban mantener las siguientes relaciones con los métodos de la clase base:

1. La clase derivada debe tener un método correspondiente a cada método de la

clase base. Este método puede heredarse directamente de la clase base o

sobrescribirse.

2. Cada método de la clase derivada que se corresponda a un método de la clase

base debe requerir lo mismo o menos que la clase base. Es decir, si se

sobrescribe un método heredado de la clase base, las precondiciones del

método deben ser más débiles o permisivas que las del método de la clase

base. Dicho de otro modo, si se sobrescribe en una clase derivada un método

heredado de la clase base se debe garantizar que el nuevo método funcione

en las mismas condiciones y recibiendo los mismos argumentos que el

117

Page 16: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

método heredado. El nuevo método no puede ser más restrictivo que el

método heredado.

3. Cada método de la clase derivada que se corresponda a un método de la clase

base debe garantizar lo mismo o más que la clase base. Es decir, si se

sobrescribe un método heredado de la clase base, las postcondiciones del

método de las clases derivada deben ser más fuertes o rigurosas que las

heredadas de la clase base. Dicho de otro modo, el método de la clase

derivada no debe comprometerse a ofrecer mayores resultados o resultados

diferentes; sólo debe comprometerse a hacer lo que hace el método de la

clase base, garantizando también las propiedades adicionales. Por ejemplo, si

un método de la clase base devuelve un número mayor que el argumento que

recibe, un método de una clase derivada podría devolver un número primo

mayor que el argumento. Pero no estaría permitido que el método de la clase

derivada devolviese un número menor o igual que el argumento.

4. Está permitido que la clase derivada introduzca nuevos métodos adicionales

que no aparezcan en la clase base.

Las clases derivadas deben garantizar también 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 restricción para que

el atributo sea mayor o igual que cero, la clase derivada debe garantizar que se cumple

esta restricción y que el atributo en la clase derivada también tendrá siempre un valor

mayor o igual que cero.

Como podemos ver, cumplir con el principio de sustitución de Liskov es fácil,

siempre que no modifiquemos ni sobrescribamos los atributos y métodos que las clases

hijas heredan de sus madres y nos limitemos sólo a añadir nuevos atributos y métodos

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 más 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

Page 17: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.11 Contraejemplo de la herencia. El cuadrado no es un rectángulo

Un ejemplo clásico de uso peligroso de la herencia es la relación entre cuadrado

y rectángulo, analizada por Barbara Liskov en su trabajo sobre el principio.

Desde el colegio, todos sabemos que un rectángulo es una figura geométrica de

cuatro lados iguales dos a dos. Y que un cuadrado “es un” rectángulo con todos los

lados iguales.

La herencia se suele considerar como una relación “es un”, por tanto podría

parecernos natural que la clase Cuadrado heredase de la clase Rectángulo, ya que, al fin

y al cabo un cuadrado es un caso particular de rectángulo.

La clase Rectángulo básicamente podría tener un par de atributos, ancho y largo,

que son suficientes para definir el rectángulo. Nuestra clase tendría un método

establecerTamaño que recibe como argumentos el ancho y el largo del rectángulo que

queremos definir.

+establecerTamaño(int ancho, int largo)()

-int ancho-int largo

Rectangulo

Figura 4. 8. Clase Rectángulo

Si la clase Cuadrado hereda de la clase Rectángulo, entonces Cuadrado tendrá

dos atributos, largo y ancho, y un método establecerTamaño que recibe como

argumentos el largo y el ancho del cuadrado. Ni los atributos ni los métodos heredados

de Rectángulo 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 método heredado tendremos que añadir algunas restricciones:

119

Page 18: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

2. el método establecerTamaño tendrá como precondición que el valor del

argumento ancho sea igual al valor del argumento largo. En caso contrario

se lanzará una excepción o un mensaje de error.

+operación1()+establecerTamaño(int ancho, int largo)()

-int ancho-int largo=ancho

Cuadrado

//precondicion: ancho=largo

+establecerTamaño(int ancho, int largo)()

-int ancho-int largo

Rectangulo

Figura 4. 9 Relación de herencia entre cuadrado y rectángulo

Preguntémonos ahora que ocurriría si sustituimos la clase Rectángulo por la

clase Cuadrado en algún lugar del código donde se use Rectángulo. ¿Podría un cliente

de Rectángulo 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 Rectángulo el resultado será impredecible.

¿Qué es lo que está ocurriendo en términos del principio de sustitución de

Liskov? El principio no se cumple porque los atributos y métodos de Cuadrado son más

restrictivos que los de Rectángulo. Cuadrado no es subtipo de Rectángulo. Por tanto, si

no queremos tener resultados inesperados no deberíamos utilizar la herencia de

Cuadrado a Rectángulo. Herencia que, por otra parte, no aporta ninguna ventaja al

diseño, ya que Cuadrado necesita sobrescribir todo lo que hereda de Rectángulo para

funcionar.

120

Page 19: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Nuestra intuición nos ha llevado a pensar que Cuadrado debería heredar de

Rectángulo porque conceptualmente un cuadrado es un rectángulo. Sin embargo, hemos

visto que esta clasificación, a pesar de ser cierta, no aporta ninguna ventaja a nuestro

diseño y sí puede traernos serios inconvenientes. La herencia puede ser contradictoria

con la clasificación usual. En los diseños orientados a objetos, copiar la realidad o

dejarse arrastrar por ella, no siempre es una buena idea. Un cuadrado es un rectángulo

en geometría, pero la clase Cuadrado no es un subtipo de la clase Rectángulo en el

software.

4.12 Las clases abstractas

El ejemplo del cuadrado y el rectángulo demuestra que la relación geométrica de

tipo y subtipo entre rectángulo y cuadrado no es aplicable a una relación software entre

la clase Rectángulo 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 rectángulos, hay que aprovechar la relación tipo – subtipo, pero de

otra forma: definiendo a las clases Cuadrado y Rectángulo como subtipos (hermanos)

de un tipo que se podría denominar Figura. En la Figura 4. 10 se muestra esta relación,

ampliada con la clase Paralelogramo (el “supertipo” geométrico de rectángulo y

cuadrado, aquí obligado a ser un subtipo más).

121

Page 20: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Figura 4. 10 Una relación prudente entre las clases Cuadrado, Rectángulo y

Paralelogramo

La relación de la Figura 4. 10 permite el uso intercambiable de objetos cuadrado,

rectángulo y paralelogramo porque todos son subtipos de un tipo. La dificultad de la

relación es la definición del código de los métodos del tipo Figura. Pero el enfoque de

objetos salva esta dificultad, dando otro paso más en su capacidad de expresar

ambigüedad: permite que los métodos del tipo sean sólo nombres, nada de código

interior. Es decir, que sean una abstracción de los métodos homónimos de los subtipos.

Este hecho da lugar a las siguientes definiciones:

Se denomina método abstracto al método que sólo expresa la cabecera y carece

de código interno. Dicho de otro modo, un método que está declarado pero no

implementado [Booch 94]. Por ejemplo, los métodos pintar, mover, ampliar y

área, de la clase Figura. También se les llama métodos virtuales o diferidos. En

UML los métodos abstractos se escriben con letra cursiva. En el presente texto,

la letra cursiva tiene el objetivo de resaltar, no indica abstracción.

Se denomina clase abstracta a la clase que contiene al menos un método

abstracto porque refleja la abstracción de ese método. Por ejemplo, la clase

Figura. También se les llama clases virtuales o diferidas. Mientras que una clase

es la definición de un conjunto de objetos, una clase abstracta es la definición de

122

Figura

pintar ampliar moverárea

Paralelogramo

plargoanchoángulo

pintar ampliar moverárea

Cuadrado

Plargo

pintar ampliar moverárea

Rectángulo

pintar ampliar moverárea

plargoancho

color

Page 21: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

un conjunto de clases. Es una abstracción de abstracciones que eleva la

capacidad de expresar ambigüedad del enfoque estructurado. La carencia de

código, en al menos un método, 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 colección de operaciones que tiene un

nombre y que se usa para especificar un servicio de una clase. Es un paso más

elevado en el camino de la ambigüedad en forma de abstracción. Una interfaz

carece de atributos. UML realza la importancia de este tipo de abstracción 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 española. Pero la traducción de la

palabra inglesa es interfaz, puesto que “face” significa cara, faz. Por cierto,

como la palabra faz es femenina, la palabra interfaz también es femenina y debe

ser precedida por el artículo “la”.

La clase Figura contiene solo el atributo color; prescinde de otros atributos que

pueden ser comprometedores (restrictivos), por ejemplo, p (posición) y largo. Si se

coloca el atributo largo, la clase Figura se compromete sólo con figuras geométricas

que tengan algo recto, para que se pueda definir largo. El atributo p (posición) parece

más universal porque cualquier figura geométrica en la pantalla debe tener una posición.

Es cierto, pero el punto de la figura que determina la posición puede tomar distintos

nombres, por ejemplo “vértice superior izquierdo” en los paralelogramos y similares, y

“centro” en los círculos. 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 ambigüedad.

4.13 Las clases generales, una solución alternativa, pero…

Una solución alternativa al problema de los cuadrados y rectángulos es

aprovechar la clase Rectángulo para que también opere con cuadrados, pero como

rectángulos, sin distinguirlos. La clase Rectángulo no diferencia entre cuadrados y

rectángulos, pero los humanos podrían hacerlo. Con una sola clase se ha resuelto el

problema de los cuadrados y los rectángulos.

123

Page 22: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La solución anterior se puede extender a la clase Paralelogramo y ahorrarse la

clase Rectángulo. Ahora, una sola clase sirve para resolver tres problemas: el cuadrado,

el rectángulo y el paralelogramo, que son casos particulares unos de otros desde el

punto de vista de la geometría. Figura 4. 11

Figura 4. 11 Generalización de una clase

Un paso más allá conduce a la clase polígono irregular (por qué limitarse al

regular) que se consigue con reajustes de atributos y métodos (cada vez más generales).

Y continuando con la tentación (para qué tantas clases), se podría disponer de una única

clase Figura* (para distinguirla de la otra) capaz de operar con cualquier figura

geométrica. La Figura 4. 11 ilustra un posible proceso de generalización que conduce a

la clase Figura*. Se ha cambiado la disposición usual para realzar el aumento de los

atributos a medida que la clase aumenta en generalidad. La confusión que produce la

figura es un efecto colateral, que también se produce cuando se trabaja con clases

generales.

124

largo P

ancho

ángulo

Cuadrado

Rectángulo

Paralelogramo

Figura*

Page 23: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Pero, ¿para qué todo en una sola clase? Una sola clase no es necesariamente más

simple que varias clases. La complejidad no se reduce por empaquetarla en un solo

elemento. Generalmente sucede al revés. La complejidad de la clase Figura* es mayor

que la complejidad total de las clases separadas y además es más embarazosa de

manejar.

Por ejemplo, para que la clase Rectángulo opere con cuadrados hay que darle un

valor al atributo ancho imprescindible para la clase, pero innecesario para el humano.

La clase Paralelogramo exigiría además, el ángulo.

Otra variante sería definir los atributos de Rectángulo en términos de los vértices

de la diagonal y el método pintar, de la clase Rectángulo, pintaría la figura inscrita en

esos vértices, sin distinguir cuadrado de rectángulo. La responsabilidad de distinguir se

traspasa a la persona, cliente de la clase Rectángulo, que debe calcular las coordenadas

del otro vértice, tanto para el rectángulo como para el cuadrado. Si se equivoca en la

operación de cálculo la figura sale mal. La situación es peor si la clase Paralelogramo

se utiliza también para cuadrados y rectángulos. No obstante, puede ser útil disponer de

una clase donde la figura se describa a través de vértices o puntos.

Comúnmente, mientras mayor sea la generalidad de la clase que realiza la tarea,

más se complica su desarrollo, mantenimiento, manipulación, 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 más complejo montado en una sola pieza donde todo está relacionado con

todo. De la manipulación ya se ha hablado. Precisa información redundante cuando

opera con casos particulares más simples, muchas veces exige trabajo extra de quienes

usan la clase y por estas dos causas es más 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,…

según 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. Además, cuando se trate de

mejorar alguno de sus usos, se puede perjudicar otro porque todos ellos están muy

125

Page 24: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

ligados, en la misma piedra. Los humanos fueron especializando los distintos usos de la

piedra en instrumentos particulares. También conviene hacerlo en el software.

4.14 Las clases particulares, beneficios y problema

Los instrumentos particulares sólo exigen la información específica que

necesitan; facilitan el desarrollo porque se pueden construir y probar uno a uno, o al

unísono 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 más de uso específico 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 ambigüedad, solución al problema de la diversidad

El enfoque de objetos facilita la resolución del problema de la diversidad a

través de sus recursos para expresar y manejar ambigüedad. La clase Figura es una

abstracción 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

Herramientas especializadasHerramienta general

cliente cliente

Figura*Figura

Page 25: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La Figura 4. 12 contrasta del diseño de Figura y sus subtipos con el diseño de la

clase Figura*. Ambos diseños muestran una cara uniforme a sus elementos clientes. El

primero por ser una abstracción y el segundo por ser una implementación de una

herramienta general. La cara de la abstracción es simple porque no se compromete con

los detalles, muestra sólo la esencia. La cara de Figura* es compleja porque tiene que

expresar toda la información 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 específica. El objeto de la clase Figura* es el

encargado de realizar la tarea, cualquiera que sea la figura. La clase Figura es una

abstracción, mientras que la clase Figura* es un elemento concreto, particular, aunque

implemente un mecanismo general.

4.16 La ambigüedad es la clave, no la división

El contraste de ambos diseños puede sugerir que los beneficios de Figura y sus

subtipos deriva de aplicar el principio de divide y vencerás o de su versión software:

modularizar. Pero no es así, ni tampoco está asociado con cohesión y acoplamiento. La

simplificación se obtiene mediante la especificidad y la introducción de ambigüedad.

La clase Figura* es un módulo cohesivo con bajo acoplamiento. Es un módulo

bien diseñado. Su división en partes más pequeñas no daría lugar a los subtipos porque

la división de un mecanismo general en trozos no produce mecanismos particulares.

Incluso, la propia clase Figura* podría ser un subtipo más de Figura. Coexistiría 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

círculo y el triángulo existieron como concepto antes que el concepto general de figura.

La fuente de la clase Figura es la abstracción, la expresión de lo común a todas

las figuras del sistema software. La introducción de este elemento ambiguo es la

decisión de diseño que permite homogeneizar la diversidad, disponer de un acceso

común a todas las clases particulares.

La ambigüedad que expresa la abstracción simplifica la complejidad descriptiva

porque reduce la cantidad de elementos que se necesitan para describir el diseño. La

127

Page 26: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

existencia de la clase Figura describe que el sistema se ocupa de figuras. Sin esta clase

habría que decir que el sistema se ocupa de cuadrados, rectángulos, … Además, la

ambigüedad simplifica también la complejidad de incertidumbre porque simplifica el

trabajo de reajuste presente y futuro del sistema.

4.17 La herencia múltiple

Hasta ahora se ha estudiado la herencia simple de un solo progenitor

(partenogenética), pero también existe la herencia de varios progenitores.

Se denomina herencia múltiple a la herencia donde una subclase tiene más de

una superclase directa. La Figura 4. 13 ilustra la herencia múltiple.

Figura 4. 13 Herencia múltiple

Haciendo un guiño a la biología, la herencia es como una reproducción 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,

refiriéndonos a la Figura 4. 13, la clase C hereda todos los atributos y métodos de la

clase A y, también, todos los atributos y métodos de la clase B.

Algunos textos utilizan este mecanismo para obtener elementos software

híbridos. Otros, como recurso de clasificación mixta. Pero, si la herencia simple es

128

C

ab

mamb

B

b

mb

A

a

ma

Page 27: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

peligrosa, la herencia múltiple es múltiplemente peligrosa. Por ejemplo, a menudo se

ilustra la herencia múltiple a través de las superclases Barco y Automóvil para obtener la

clase Anfibio. Un problema es la distorsión del sujeto que se pretende representar

porque el anfibio es barco cuando está en el agua y automóvil cuando está en tierra,

mientras que la clase Anfibio es Barco y Automóvil a la vez porque la herencia es un

mecanismo estático (en tiempo de compilación).

Si el mecanismo hereditario fuese dinámico, en tiempo de ejecución, un objeto

pudiera ser de una clase ahora y de otra después. No habría confusiones porque no sería

de dos clases en el mismo momento. Aunque los lenguajes comerciales de

programación orientada a objetos todavía no incorporan una herencia dinámica, es

posible conseguir efectos semejantes con algunas técnicas, referidas por [Fowler 97].

Otro problema es solapamiento del contenido “genético” heredado. Por ejemplo,

la Figura 4. 14 muestra el resultado de la herencia múltiple de las clases Gallina y

Tiburón para conseguir una clase con propiedades de correr y nadar.

Figura 4. 14 Herencia múltiple con problema.

La dificultad de la clase Gallirón está en el atributo boca porque tiene dos

especificaciones distintas. Una manera de resolver el solapamiento genético es con la

herencia dinámica. Otra, sería 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

Gallirón

boca boca

correr nadar

Tiburón

boca

nadar

Gallina

boca

correr

Page 28: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

El lenguaje Java adopta esta última solución de forma radical. Sólo permite la

herencia múltiple de las denominadas interfaces (clases abstractas puras que contienen

porque suprime los atributos y reduce las operaciones a simples declaraciones. La

implementación de las operaciones, en clase que hereda, resuelve las colisiones

potenciales. Esta modalidad de la herencia múltiple es una forma segura y útil para

dotar a una clase con más de una cara (interfaz).

4.18 Aproximación al patrón adaptador

La doble cara de una clase facilita adaptar sus operaciones a la cara que

necesitan otras clases para usar esas operaciones. El denominado patrón adaptador

aprovecha el efecto de la doble cara que otorga la herencia múltiple. Figura 4. 15

Figura 4. 15 Variante del patrón adaptador

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

una implementación más del método i, declarado en la clase abstracta pura B. Entonces,

se diseña una clase C que herede de las clases A y B el método n y la declaración del

método i respectivamente. Como ambos elementos se han juntado en la clase C, basta

con definir que la implementación del método i consiste en invocar al método n para

conseguir que n sea una i más. Esta última técnica se denomina envolver.

130

C

inm

B

i

A

mn

M

i

G

i

i{

invocar n

}

Diseño para conseguir que n sea otro i

Page 29: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.19 La herencia vista desde el código, un ejemplo

En capítulos anteriores se diseñaron pequeños sistemas para trabajar con

triángulos, círculos y rectángulos. Ahora se quiere integrar estos sistemas en uno solo. A

continuación se muestran los diseños de cada clase.

+Circulo()+paint(entrada Graphics g)+mover(entrada int desplazamientox, entrada int desplazamientoy)+ampliar(entrada int zoomout)+reducir(entrada int zoomin)+borrar()

-int centrox-int centroy-int radio-Color color-boolean haydatos

Circulo

FormularioCirculo

Figura 4. 16 Clase Círculo

+Triangulo()+paint(entrada Graphics g)+mover(entrada int desplazamientox, entrada int desplazamientoy)+ampliar(entrada int zoomout)+reducir(entrada int zoomin)+borrar()

-int verticesuperiorx-int verticesuperiory-int base-int altura-Color color-boolean haydatos

Triangulo

FormularioTriangulo

Figura 4. 17 Clase Triángulo

131

Page 30: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

+Rectangulo()+paint(entrada Graphics g)+mover(entrada int desplazamientox, entrada int desplazamientoy)+ampliar(entrada int zoomout)+reducir(entrada int zoomin)+borrar()

-int origenx-int origeny-int base-int altura-Color color-boolean haydatos

Rectangulo

FormularioRectangulo

Figura 4. 18 Clase Rectángulo

Las clases Círculo, Rectángulo y Triángulo son distintas entre sí porque tienen

atributos distintos y comportamientos distintos: un círculo se pinta de forma distinta que

un rectángulo y que un triángulo; también la forma de ampliarlo, reducirlo o moverlo es

distinta a la del rectángulo y el triángulo. Pero todas ellas son figuras, por tanto se podrá

diseñar una abstracción que exprese la esencia de interés y omita los detalles que las

diferencian. La clase Figura, discutida antes, puede ser un punto de partida para este

diseño particular.

Las cabeceras de las operaciones de cada figura coinciden porque han sido

diseñadas con una disciplina, manteniendo un estilo. Si las cabeceras no coincidieran

sería posible aplicar una técnica de adaptación para uniformarlas. La coincidencia es

necesaria para conseguir que las operaciones sean polimórficas cuando se relacionen

con las operaciones homónimas de la clase Figura. Figura 4. 19

-Color color-boolean haydatos

Figura

+Figura()+paint(entrada Graphics g)+mover(entrada int desplazamientox, entrada int desplazamientoy)+ampliar(entrada int zoomout)+reducir(entrada int zoomin)+borrar()

+Figura()

+borrar()

132

Page 31: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Figura 4. 19 Clase abstracta Figura

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

inicio, se considera que será común a todos los subtipos, independientemente de la

figura. Una figura se borra pidiéndole que se pinte con el color de fondo. La

implementación de borrar() estará en la clase madre y las hijas heredarán el código de

ella. La presencia de este método hace que la clase Figura sea abstracta, pero no

abstracta pura o interfaz. Además, en esta clase se han añadido 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. También está

escritos con la misma letra los métodos abstractos o virtuales paint(), mover(), ampliar()

y reducir().

La Figura 4. 20 muestra el diseño del diagrama de clases del sistema.

Figura 4. 20 Diagrama de clases de Figura polimórfica

Entre la clase Ventana y la clase Figura existe una relación de agregación

porque la clase Ventana contiene un atributo Figura de tipo Figura. Este atributo será

en un momento dado un objeto Círculo, Triángulo o Rectángulo. Pero como todos

133

Page 32: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

comparten la misma especificación, la clase Ventana puede tratarlos a todos por igual

utilizando la especificación de Figura, sin necesidad de conocer el tipo de figura,

excepto en el instante de la creación de los objetos.

Un constructor está ceñido a crear objetos de una clase específica, no puede

crear objetos de otras clases por su especificidad. La creación de un objeto no es una

operación polimórfica. De manera que el objeto creador establece una fuerte relación

unívoca hacia el objeto creado. Para debilitar esta relación se utilizan diversas técnicas

según sea el caso. Algunas de ellas serán 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 Círculo, Rectángulo y Triángulo

según elija, en la pantalla, el usuario del sistema. Por eso hay una relación de

dependencia de Ventana a cada una de las clases Círculo, Triángulo y Rectángulo. Una

vez que el usuario haya elegido la figura se asignará al atributo figura el objeto creado y

Ventana podrá trabajar con él a través de la especificación de Figura. Se abre la puerta

al uso del polimorfismo.

Volviendo al principio de sustitución de Liskov, observamos que en este caso

Círculo, Triángulo y Rectángulo son subtipos de Figura ya que podemos sustituir

Figura por cualquiera de ellos en el código de Ventana y el resultado será correcto.

134

Page 33: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.20 El polimorfismo en Java

Veamos cómo se implementa el polimorfismo en el lenguaje Java. A

continuación mostramos el código de las clases Ventana, Figura, Círculo, Triángulo y

Rectángulo del ejemplo anterior. El código 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; private JButton circulo, rectangulo, triangulo, ampliar, reducir, arriba, abajo, izqda, dcha; public Ventana() { //Pintar la ventana vacía 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

Declaramos en ventana el atributo figura de la clase Figura

Page 34: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

//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 añadirlos 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 añadirlos 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); //Añadir 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

Page 35: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

}

public abstract class Figura extends JPanel{ protected Color color; protected boolean haydatos=false; public Figura() { } public void paint(Graphics g){

137

Declaramos la clase abstracta Figura

El método paint debería ser abstracto, pero para poder utilizar el API swing necesitamos que esté implementado en todas las clases.

Page 36: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

Declaramos los métodos abstractos mover, ampliar y reducir

Declaramos la clase Círculo que hereda de Figura

Implementamos el método paint

Page 37: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

139

Implementamos el método ampliar

Implementamos el método reducir

Implementamos el método paint

Implementamos el método mover

Declaramos la clase Rectángulo que hereda de Figura

Page 38: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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

Declaramos la clase Triángulo que hereda de Figura

Implementamos el método ampliar

Implementamos el método reducir

Implementamos el método mover

Page 39: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

color=formulario.obtenerColor(); 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; } }

141

Implementamos el método paint

Implementamos el método ampliar

Implementamos el método reducir

Implementamos el método mover

Page 40: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

}

4.21 Ejercicio

Reutilice el la solución de figuras geométricas para dibujar una cara. El aspecto

de la cara se muestra a continuación. Puede suponer que el contorno de la cara es un

círculo, que los ojos también son círculos y que la boca es un rectángulo muy estrecho.

142

Page 41: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Una solución

Pensar en objetos es asociar a un objeto la tarea de dibujar la cara. Sería un

objeto compuesto de otros objetos; uno por cada componente de la cara. Por ejemplo:

contorno, es un objeto de la clase Círculo asociado con el

contorno de la cara.

ojoderecho y ojoizquierdo, son dos objetos de la clase

Círculo asociados con los ojos.

boca, es un objeto de la clase Rectángulo asociado con la

boca.

Además conviene considerar como atributos del objeto Cara

centrox y centroy, son dos números enteros para indicar

las coordenadas del centro de la cara. Servirán para situar la cara en la pantalla.

tamanyo, un número entero para indicar el tamaño 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 geométricas,

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

el diagrama de clases.

143

Page 42: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

+Ventana()-asignarLookAndFeel()-configurarGUI()-setCloseClick()+paint(entrada Graphics g)+main(entrada String[] args)

-Cara cara

Ventana

Circulo

Figura

TrianguloRectangulo

+Cara()+paint(entrada Graphics g)

-Circulo contorno-Circulo ojoderecho-Circulo ojoizquierdo-Rectangulo boca-int centrox-int centroy-int tamanyo-Color color

Cara

Figura 4. 21 Diagrama de clases de Pintar Cara

Las relaciones de agregación entre las clases Cara, Círculo y Rectángulo

expresan que los objetos de la clase Cara están compuestos por objetos de las clases

Círculo y Rectángulo. Estas relaciones reflejan el tipo de los atributos contorno,

ojoderecho, ojoizquierdo y boca.

Veamos a continuación el código 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; //tamaño 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

Page 43: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

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 solución pinta la cara reutilizando la estructura de figuras desarrollada

anteriormente. La Figura 4. 22 muestra el resultado de la ejecución del programa.

Figura 4. 22 Resultado de Pintar Cara

145

Page 44: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

4.22 Otro ejercicio

La cara que pintamos no les gustó a los niños y propusieron que el contorno

fuese un cuadrado. Haga las modificaciones necesarias en el diseño para satisfacer la

nueva solicitud. De paso, evalúe la facilidad que ofrece su diseño anterior para ajustarse

a este cambio.

Una solución posible, consiste en reutilizar el diseño anterior modificando el

atributo contorno de la clase Cara para que sea un objeto de la clase Rectángulo en

lugar de Círculo. Como ambas clases, Rectángulo y Círculo, tienen un método llamado

paint(), no sería necesario modificar el método paint() de Cara, ya que el mensaje

contorno.paint() sigue siendo válido.

146

Page 45: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La búsqueda de la plasticidad, un cambio del diseño

El cambio o reajuste de requisitos es un fenómeno muy frecuente en el software,

tanto que resulta ser uno de los problemas más importantes (y también rentables) del

desarrollo de software.

La solución anterior al ejercicio de las caras resolvió la situación de modo

directo, sin pensar más allá, porque lo que no se sabe, no se sabe. De lo contrario en el

software se contratarían oráculos, videntes, cualquiera que sea capaz de anticiparse (con

exactitud), pero no dan resultado. La solución 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 diseñar una solución de mayor plasticidad

(deformable), que admita como componente de la cara cualquier figura. Se trata de

introducir ambigüedad en el diseño.

Un paso en esa dirección es asignar los atributos contorno, ojoizquierdo,

ojoderecho y boca de tipo Figura, usando la palabra tipo en el sentido estricto de

Liskov. La intención es aprovechar la posibilidad de polimorfismo que ofrecen las

operaciones de la clase Figura.

El diseño de los componentes de la cara como objetos de la clase Figura

introduce suficiente ambigüedad para componer la cara con cualquier figura de las

disponibles actualmente o en el futuro.

Otro paso que aumenta la plasticidad del diseño 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 añade ambigüedad al diseño porque

los nombres de los componentes se sustituyen por ordinales: el primer componente, el

segundo, etc. Gracias a esta ambigüedad se gana en igualdad de proceso y en facilidad

de modificación. Pero la ambigüedad también puede dificultar la comprensión por un

efecto de desconcierto. Casi nada es gratis. Para reducir el posible efecto negativo de la

ambigüedad se han utilizado variables temporales con los nombres de los componentes.

147

Page 46: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Sin embargo, a pesar de toda la ambigüedad introducida en el diseño, se

mantienen dependencias directas de Cara hacia Círculo y Rectángulo a causa de las

operaciones de creación de objetos. Sólo cuando los creamos necesitamos declarar su

tipo específico Círculo y Rectángulo (recordemos que no pueden crease objetos de

clases abstractas como Figura). En la aproximación a los patrones se verá una solución

que reducen estas ligaduras.

Una vez creados los objetos podemos tratarlos a todos por igual usando los

métodos de la clase Figura. Por eso ahora, en el método 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 método paint() del elemento concreto utilizando la cabecera que

heredan todos de Figura.

La Figura 4. 23 muestra el diseño del diagrama de clases de esta otra nueva

solución de más plasticidad.

+Ventana()-asignarLookAndFeel()-configurarGUI()-setCloseClick()+paint(entrada Graphics g)+main(entrada String[] args)

-Cara cara

Ventana

Circulo

Figura

TrianguloRectangulo

+Cara()+paint(entrada Graphics g)

-Figura [ ] facciones-int centrox-int centroy-int tamanyo-Color color

Cara

Figura 4. 23 Diagrama de clases de Pintar Cara polimórfico

El diseño de la Figura 4. 23 aumenta la facilidad de extensión y modificación del

sistema (plasticidad) pero conserva la función del sistema; todavía los contornos de las

caras son círculos. Este ejemplo ilustra la siguiente definición:

148

Page 47: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

Se denomina factorización a las modificaciones que se realizan en los diseños

para mejorar alguna cualidad interna del sistema sin variar sus funciones.

Veamos el código Java de esta nueva clase Cara.

public class Cara extends JPanel{ private Vector facciones; //Vector de objetos Figura que contiene las facciones de la cara: //contorno, ojoderecho, ojoizquierdo y boca private int tamanyo=200; //tamaño 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 /** Crear una nueva instancia de Cara */ public Cara() { Figura contorno, ojoizquierdo, ojoderecho, boca; facciones = new Vector(); //crear el contorno y añadirlo a las facciones contorno = new Circulo(centrox, centroy, tamanyo, color); facciones.add(contorno); //crear los ojos y añadirlo a las facciones ojoizquierdo = new Circulo(centrox-(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK ); facciones.add(ojoizquierdo); ojoderecho = new Circulo(centrox+(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK ); facciones.add(ojoderecho); //crear la boca y añadirlo a las facciones boca = new Rectangulo(centrox-(tamanyo/4), centroy+(tamanyo/2), tamanyo/2, 2, Color.BLACK); facciones.add(boca); } public void paint (Graphics g) { Figura figura; super.paint(g); //lo utilizamos para pintar la ventana de Windows //pintamos los elementos del vector facciones Iterator i=facciones.iterator(); while (i.hasNext()){ figura= (Figura) i.next(); figura.paint(g); } } }

149

Page 48: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La solución del rectángulo

Dada la plasticidad del diseño nuevo, el cambio del contorno de la cara se

resuelve sólo modificando la línea en la que creamos el objeto contorno para crear un

Rectángulo en lugar de un Círculo. Veamos este cambio sobre el código.

public class Cara extends JPanel{ private Vector facciones; //Vector de objetos Figura que contiene las facciones de la cara: //contorno, ojoderecho, ojoizquierdo y boca private int tamanyo=200; //tamaño 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 /** Crear una nueva instancia de Cara */ public Cara() { Figura contorno, ojoizquierdo, ojoderecho, boca; facciones = new Vector(); //crear el contorno y añadirlo a las facciones contorno = new Rectangulo(centrox-tamanyo,centroy-tamanyo,2*tamanyo,2*tamanyo,color); facciones.add(contorno); //crear los ojos y añadirlo a las facciones ojoizquierdo = new Circulo(centrox-(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK ); facciones.add(ojoizquierdo); ojoderecho = new Circulo(centrox+(tamanyo/3), centroy-(tamanyo/4), 10, Color.BLACK ); facciones.add(ojoderecho); //crear la boca y añadirlo a las facciones boca = new Rectangulo(centrox-(tamanyo/4), centroy+(tamanyo/2), tamanyo/2, 2, Color.BLACK); facciones.add(boca); } public void paint (Graphics g) { Figura figura; super.paint(g); //lo utilizamos para pintar la ventana de Windows //pintamos los elementos del vector facciones Iterator i=facciones.iterator(); while (i.hasNext()){ figura= (Figura) i.next(); figura.paint(g); } } }

150

Page 49: 04CursoOO_herencia

Curso de OO dirigido por La herencia, más aumento de la ambigüedadla introducción de ambigüedad

La Figura 4. 24 muestra el resultado de la ejecución del programa.

Figura 4. 24 Resultado de Pintar Cara

Bibliografía

[Booch 94] Grady Booch, “Object Oriented Analysis and Design” Ed. Benjamin

Cummings Publishing, 1994

[Rumbaugh 95] James Rumbaugh et al. “Modelado y Diseño 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