Sie sind auf Seite 1von 13

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

HERENCIA MLTIPLE EN JAVA

Oscar A. Len, Mariana Brachetta, Julio Monetti Ctedra Paradigmas de Programacin Departamento de Ingeniera en Sistemas de Informacin Universidad Tecnolgica Nacional Facultad Regional Mendoza. Coronel Rodrguez 273. 5500 Mendoza. www.frm.utn.edu.ar Tel. 0261 4239596. oleon@frm.utn.edu.ar, mbrachetta@mendoza.edu.ar, monetti@uncu.edu.ar

RESUMEN
En el artculo se comentan algunos aspectos de la utilizacin de relaciones de herencia mltiple, para el desarrollo de software orientado a objetos. En particular se analiza una alternativa de implementacin de este tipo de relaciones en el lenguaje Java. Dado que el mismo no permite implementar relaciones de herencia mltiple en forma directa, las mismas se "simulan" mediante la utilizacin de interfaces, lo cual se muestra a travs de un ejemplo, en el cual se discuten algunos aspectos de la implementacin. Tambin se analiza el modo de soportar caractersticas como reusabilidad, polimorfismo, redefinicin de mtodos y definiciones de mtodos forwarding, y se comentan algunas de las desventajas que presenta.

PALABRAS CLAVE
Herencia Mltiple. Lenguaje Java. Programacin Orientada a Objetos. Patrones.

11

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

INTRODUCCIN
Uno de los objetivos de la programacin orientada a objetos, es incrementar la productividad en el desarrollo de software. Uno de los modos de lograr sto, es mediante la reutilizacin de cdigo existente, para lo cual las relaciones de herencia son uno de los medios ms utilizados. Un buen diseo del esquema de relaciones de herencia resulta crtico, ya que si el mismo est bien hecho facilitar la reutilizacin de cdigo, permitiendo mantener al mismo tiempo el control del desarrollo del software. Por otra parte, un mal diseo, har que la reutilizacin de cdigo se vea dificultada o impedida. El diseo del lenguaje Java, imposibilita pensar el diseo de un programa aplicando herencia mltiple (Eckel, 2000), como se podra hacer por ejemplo en C++. Java permite nicamente una forma cercana al concepto de herencia mltiple, a travs de la implementacin de interfaces. Esta restriccin de los diseadores del lenguaje, les ha permitido simplificar la implementacin del compilador. Cuando se utilizan lenguajes como C++, frecuentemente se hace un uso indebido de las relaciones de herencia mltiple, lo cual hace que el mismo pierda integridad. En Java esto no ocurre, ya que no ofrece soporte para este tipo de relaciones. Si bien las relaciones de herencia mltiple, aparecen muy pocas veces frente a las de herencia simple, ambos esquemas tienen ventajas y desventajas (Ghan Bir Singh, 1995); en ocasiones se presentan situaciones en las cuales la utilizacin de relaciones de herencia mltiple resulta conveniente.

HERENCIA MLTIPLE
Dentro de los lenguajes O.O. (orientados a objetos) existen algunos que soportan herencia mltiple, como C++ (Stroustrup, 2000) y Eiffel (Meyer, 2001); en cambio otros no, como en el caso de Smalltalk y Java. Si bien, la mayora de las situaciones pueden resolverse sin recurrir a herencia mltiple, en algunos casos es conveniente contar con esta caracterstica, a fin de lograr una mejor implementacin de la aplicacin. Desde el punto de vista del diseo de un lenguaje, soportar herencia mltiple complica la tarea de implementacin, ya que hay que prever cmo resolver situaciones como la que se muestra en la Figura 1, mediante el diagrama UML de relaciones de herencia mltiple entre las clases A, B y C (Fowler y Scott 2000). Cuando un objeto de clase C tiene que acceder al atributo p, el lenguaje debe tener algn medio de discriminar a cul atributo p se quiere referir, si al de la clase A o la B.

Figura 1: Herencia mltiple


12

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

Una situacin ms complicada es la que se muestra en la Figura 2, que presenta un caso de herencia mltiple con un ancestro comn. El atributo p se hereda a travs de dos ramas diferentes, entonces un objeto D podra tener dos copias del mismo atributo, una la que hereda por la rama de B y otra por la de C. Lenguajes como C++ y Eiffel, tienen diferentes abordajes para resolver situaciones como estas.

Figura 2: Herencia mltiple con ancestro comn Los problemas antes mencionados, son una consecuencia del modo de construir un objeto de una clase que hereda de otra. Una relacin de herencia, como por ejemplo entre las clases C y D, implica que un objeto de la clase D es una forma particular de C. Este tipo de vnculos entre clases, se describen con expresiones como extiende o es un, que dan la idea de que un objeto D es una forma de objeto C. As, un objeto D va a tener una parte que va a ser C, pero tambin una parte que ser B, lo cual plantea algunos problemas de ambigedad que se deben resolver (Stroustrup 2000). Los problemas comentados pueden aumentar, cuando se fuerza el concepto de herencia (Seidewitz, 1996). Por ejemplo, en ocasiones se establecen relaciones de herencia mltiple slo para reusar en una clase, capacidades de otras. Esto hace que el diseo de la aplicacin sea difcil de comprender y de mantener, ya que no existe un vnculo natural entre clases. Una relacin de herencia mltiple debe surgir sin forzarla. Un ejemplo de libro de texto de esta situacin, se muestra en la Figura 3, donde se define una clase auxiliar de docencia alumno (AuxDocAlu). Esta nueva entidad es una extensin de la clase Alumno, pero tambin cumple funciones relacionadas con la docencia, por lo que le corresponden atributos como Asignatura (en la que trabaja). Ante esto, se puede caer en la tentacin de definir una relacin de herencia mltiple con la clase Docente, pero con esto tambin hereda atributos y mtodos, que no le son propios como Sueldo, u otros que se duplican, como el Legajo, todo lo cual complica la implementacin.

13

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

Figura 3: Herencia mltiple forzada En el caso de tener que programar con un lenguaje O.O. que no soporte herencia mltiple, se tienen dos alternativas, o re-disear la aplicacin de modo tal que no aparezcan este tipo de relaciones, o se deben "simular".

HERENCIA EN JAVA
En vista de las situaciones de herencia mltiple descriptas anteriormente que se pueden presentar, parece razonable que el diseador de un lenguaje prefiera no tener que implementar dicho mecanismo. Pero en el caso de Java, tambin existe un motivo que va ms all de la conveniencia para el implementador del lenguaje. La ejecucin de un programa Java se basa en un intrprete de un seudocdigo de mquina (byte-code). Esta caracterstica es lo que ha permitido la independencia de la plataforma sobre la que se ejecuta una aplicacin Java. El uso de bibliotecas de clases en Java es un mecanismo muy flexible, dado que la mayora de los enlaces se resuelven durante el tiempo de ejecucin. As, se puede recompilar una clase, sin necesidad de hacer lo mismo con su subclase, y la aplicacin va a continuar ejecutndose en forma correcta. Esta caracterstica podra dar lugar a errores, en el caso de que existiera herencia mltiple. En el ejemplo de la Figura 1, si suponemos que en A agregamos un mtodo cuya cabecera coincide con la de un mtodo ya definido en B, y si desde C se activa el mtodo, ahora no alcanza con recompilar A, sino que ser necesario resolver la ambigedad en la activacin del mtodo desde C y recompilar tambin esta ltima clase.

SIMULACIN DE HERENCIA MLTIPLE


Analicemos un caso de implementacin en Java, basado en el esquema propuesto en la Figura 4, donde suponemos la existencia de un mtodo denominado algo(), que es redefinido para cada clase con distintos comportamientos.

14

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

Figura 4: Esquema de herencia mltiple con un mtodo redefinido en ambas ramas class A { public void algo() { System.out.println("A"); } } class B extends A { public void algo() { super.algo(); System.out.println("B"); } public void hace() { System.out.println("Hace"); } } class C extends A { public void algo() { super.algo(); System.out.println("C"); } public void otro() { System.out.println("Otro"); } } class D extends C, B { public void algo() { super.hace(); super.otro(); System.out.println("D"); } } Listado 1: Implementacin del esquema de la Figura 4

15

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

En el modelo aparecen tres conceptos fuertemente vinculados a las relaciones de herencia: reusabilidad, redefinicin de mtodos y polimorfismo (Booch, 1994). El primero de ellos debido a la reutilizacin del mtodo algo() de A, desde B y C. El segundo aparece al redefinir la implementacin de algo() a travs de toda la jerarqua de clases, en B, C y D. El tercero aparecer, al activar el mtodo antes mencionado para algn objeto de la jerarqua, en cuyo caso se debe lanzar la ejecucin del mtodo que corresponda al objeto referenciado en ese momento, de acuerdo al comportamiento que se espera por la aplicacin del concepto de polimorfismo (Cardelli y Wegner, 1985). La idea de polimorfismo se basa en ocultar bajo una interface comn, diferentes implementaciones de mtodos. As si durante la ejecucin del programa en distintos instantes de tiempo, por ejemplo con una variable del tipo A, se refieren objetos de las clases A, B, C, D, para enviarles el mensaje algo(); en cada caso se activar la implementacin del mtodo algo(), correspondiente a la clase a la que pertenece el objeto referenciado por la variable en ese momento. Esto se debe a que la implementacin del concepto de polimorfismo, se fundamenta en establecer vnculos dinmicos entre la llamada a un mtodo y su respectiva implementacin. As la resolucin sobre cul implementacin utilizar, se demora hasta el momento de la ejecucin, cuando se conoce la clase a la que pertenece el objeto referenciado durante el envo de un mensaje. Retomando el planteo realizado en el cdigo correspondiente a la Figura 4; el problema es que Java no provee medios para implementar herencia mltiple, por esto el cdigo provoca un error durante el proceso de compilacin, en el enunciado donde se indica que D hereda de C y B; como consecuencia de esto las activaciones super.otro() y super.hace() no son posibles. La alternativa a lo anterior es simular el esquema de herencia mltiple propuesto. Esta implementacin, tiene que realizarse respetando las caractersticas de reusabilidad, redefinicin de mtodos y polimorfismo, que presenta el modelo originalmente propuesto. Para llevar adelante esto, se aprovecha una caracterstica de Java, que permite heredar en forma simultnea de una clase y una interface. Estrictamente, no se trata de herencia mltiple, sino de heredar de una clase e implementar una o ms interfaces, en las cuales por definicin todos los atributos son constantes y todos los mtodos son abstractos (Cornell y Horstmann, 1996).

Simulacin de herencia mltiple


Mediante el uso de interfaces, en algunos casos, se pueden simular relaciones de herencia mltiple en Java. En la Figura 5, se muestra cmo quedara el nuevo esquema de relaciones entre clases.

16

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

Figura 5: Esquema de relaciones que implementan herencia mltiple en Java En el diseo del modelo se han incorporado nuevos elementos. Aparece una interface iB, que es implementada por las clases B y D. As, la relacin de herencia entre las clase D y B del modelo original, se ha reemplazado por la implementacin de la interface comn iB, por parte de las clases B y D, en tanto que D mantiene la relacin de herencia con C, simulndose de esta forma la herencia mltiple de las clases B y C. Adems, se ha incorporado una relacin de agregacin bidireccional entre las clases B y D. La necesidad de esto ltimo resultar evidente ms adelante, por lo que en la primera versin que se muestra, slo aparece implementada como una relacin unidireccional. class A { public void algo() { System.out.println("A"); } } interface iB { public void algo(); public void hace(); } class B extends A implements iB { public void algo() { super.algo(); System.out.println("B"); } public void hace() { System.out.println("Hace"); } } class C extends A { public void algo() { super.algo(); System.out.println("C"); }

17

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

public void otro() { System.out.println("Otro"); } } class D extends C implements iB { private iB objBase = new B(); public void algo() { objBase.hace(); super.otro(); System.out.println("D"); } public void hace() { objBase.hace(); } } Listado 2: Implementacin del esquema de la Figura 5 El comportamiento polimrfico se debe preservar. Para garantizar esto en la interface iB se han declarado todos los mtodos de acceso pblico que tiene B. Esto es necesario para que el compilador genere cdigo para enlazar dinmicamente la implementacin de dichos mtodos en las respectivas clases, con una llamada en el momento de la ejecucin. De no hacerlo, enunciados como los siguientes provocan error: iB obj; if (alguna condicin) obj = new B(); else obj = new D(); obj.algo(); Si el mtodo algo() no se declara en la interface, como una variable del tipo iB puede referenciar objetos de las clases B o D, la activacin del mtodo algo() sera invlida, ya que el compilador no conocera el smbolo algo. En cambio, al declararlo en la interface, an cuando no se conoce la clase de objeto referenciada por la variable obj, el cdigo se puede compilar. El enlace de la llamada al mtodo con su implementacin, queda demorado hasta el momento de la ejecucin. Por la relacin de herencia entre C y D, cualquier modificacin en los mtodos de C, se propaga a D. La implementacin debe producir el mismo efecto respecto de la relacin de herencia entre B y D. Por otra parte, al declarar en la interface iB el mtodo hace(), se obliga a tener que implementarlo en la clase D. El concepto de herencia implica que un objeto de una clase derivada, es una forma ms especializada de un objeto de la clase base de la cual deriva; esto hace que crear un objeto de una subclase, requiera la creacin de un objeto (subobjeto) perteneciente a la superclase (Budd, 2002). En Java esa es la funcin del mtodo super(), cuando se activa desde el constructor de una clase derivada. Por lo tanto desde el punto de vista de cmo es construido un objeto de una clase derivada, se puede decir que est compuesto por un objeto (subobjeto) de la clase de la cual deriva. Es interesante observar que esto es as, an cuando la clase de la cual se hereda sea abstracta. Por lo tanto la restriccin de no poder crear objetos de una clase abstracta, no es aplicable para la construccin del subojeto que forma parte de un objeto de una clase derivada.
18

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

Esto ltimo, produce una restriccin en el modelo presentado, ya que no es posible simular herencia mltiple a partir de clases abstractas, debido a que no sera posible crear el subobjeto necesario para simular el mecanismo de herencia. En la clase D, el enunciado super.otro() del mtodo algo(), lo que hace es activar el mtodo otro() correspondiente al objBase de la clase C, de la cual hereda. En la nueva implementacin se debe simular un efecto similar, para la otra rama de herencia. Esto se logra declarando el atributo privado objBase del tipo B, al cual se le asigna una referencia a un objeto B, que se utilizar para sustituir la activacin super.hace() por objBase.hace(). En el caso que D y B tuvieran constructores parametrizados para inicializar los objetos que se creen, la asignacin a objBase de la referencia al objeto B, debera realizarse en el constructor de D para simular el mismo efecto. La declaracin de hace() en la interface iB, obliga a implementar el mtodo en D. En el modelo original, como D no tiene definido el mtodo hace(), un objeto del tipo D directamente heredara la implementacin desde B. La implementacin del mtodo hace() en D, simula ese comportamiento. Esta es una tcnica de diseo de patrones estructurales, conocida como Adaptadores o Wrappers (envolturas) (Gamma et al, 1995). Estos patrones sirven para modificar el comportamiento de otra clase, operando como una cobertura sobre la clase encapsulada, que es la que en realidad realiza el trabajo. Los mismos pueden extender el comportamiento, modificar la interface o restringir el acceso a una clase. En general su funcin es hacer de intermediarios entre dos interfaces incompatibles, traduciendo las llamadas entre ellas. Finalmente como se puede observar, cualquier modificacin que se realice en los mtodos de la clase B, se va a propagar a la clase derivada D. La redefinicin de mtodos y el comportamiento polimrfico, se garantizan a travs de la definicin de la interface iB, y del uso de referencias a esta ltima, en lugar de hacerlo con B.

Implementacin de forwarding
Supongamos que el cdigo de las clases C y D, ha sido modificado como se muestra a continuacin, donde aparece un mtodo denominado nuevo(), que es implementado en la clase C y luego es redefinido en D. class C extends A { public void algo() { System.out.println("C"); super.algo(); } public void otro() { System.out.println("Otro"); nuevo(); } public void nuevo() { System.out.println("C: nuevo");

19

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

} } class D extends C implements iB { private iB objBase = new B(); public void algo() { System.out.println("D"); objBase.hace(); super.otro(); } public void hace() { objBase.hace(); } public void nuevo() { System.out.println("D: nuevo"); } } Listado 3: Implementacin de forwarding Para los siguientes enunciados, cuando se procese la llamada incluida en otro() de C, la implementacin del mtodo nuevo() que se activar ser la correspondiente a la clase D y no a C. Este comportamiento se debe a que el objeto que inici la secuencia de activaciones de mtodos pertenece a la clase D, y lo que se hace es activar un mtodo que se encuentra definido ms adelante (forwarding) en la jerarqua de herencia. C obj = new D(); obj.algo(); En el caso de plantearse una situacin semejante para la clase B, se debe simular el mismo comportamiento. Esto se puede hacer manteniendo una referencia inversa desde el objeto base de la clase B al objeto derivado del tipo D. Entonces del cdigo de ambas clases, para una situacin similar a la antes descripta sera el siguiente, con lo cual se completa el esquema presentado en la Figura 5. interface iB { public void algo(); public void hace(); public void nuevo(); } class B extends A implements iB { private iB objDeriv; public B() { super(); objDeriv = this; } public B(D obj) { super(); objDeriv = obj; } public void algo() { super.algo();
20

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

System.out.println("B"); } public void hace() { System.out.println("Hace"); objDeriv.nuevo(); } public void nuevo() { System.out.println("B: tres"); } } class D extends C implements iB { private iB objBase; public D() { super(); objBase = new B(this); } public void algo() { objBase.hace(); super.otro(); System.out.println("D"); } public void hace() { objBase.hace(); } public void nuevo() { System.out.println("D: tres"); } } Listado 4: Implementacin final del esquema de la Figura 5 Las referencias cruzadas se implementan, agregando el atributo objDeriv en B, que debe ser del tipo iB porque se necesitar referenciar con esta variable objetos del tipo B o D. Adems se han incorporando dos constructores, que establecen los vnculos en el momento de crear un objeto D. El constructor sin parmetros en B, se requiere por haber sobrecargado los constructores de la clase, pero adems se necesita para inicializar el atributo objDeriv con la referencia a s mismo, cuando la creacin de un objeto B no es consecuencia de la creacin de un objeto de la clase D. El mtodo nuevo() adems de en las clases, debe ser declarado en la interface. La activacin del mtodo nuevo() en B, se hace utilizando la variable objDeriv, que referencia un objeto B o D, segn corresponda.

Reutilizacin
Si se quiere crear una clase que herede de D, reutilizando mtodos definidos en la jerarqua de herencia, la tarea no resulta complicada. Por ejemplo una clase X que hereda de D, en la cual se redefinen los mtodos algo() y nuevo(). Tambin se define un mtodo propio(), que reutiliza los mtodos hace() y otro(), de las clases B y C respectivamente.

21

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

class X extends D { public void propio(){ System.out.println("Propio"); hace(); otro(); } public void algo() { propio(); } public void nuevo() { System.out.println("X: nuevo"); } } A continuacin se declara una variable del tipo A, que por ser la raz de la jerarqua de herencia, posibilita referenciar objetos pertenecientes a cualquiera de las subclases. Luego se crea un objeto del tipo X y se activa el mtodo algo(). A p = new X(); p.algo(); En este caso la salida es la siguiente: Propio Hace X: nuevo // Salida de la llamada a nuevo(), desde hace() en B Otro X: nuevo Como se puede observar los comportamientos del programa, debidos a la aplicacin de conceptos como reutilizacin, polimorfismo y forwarding, son los que cabe esperar.

CONCLUSIONES
Se ha analizado una alternativa de implementacin de herencia mltiple, en un lenguaje como Java, que no soporta este tipo de relaciones. Para esto se utiliza una interface, cuya funcin es proveer el acceso a la clase de la cual se pretende heredar en forma mltiple. Varios aspectos del tema abordado, se enmarcan dentro de lo que se denomina patrones de diseo, donde aparecen patrones como agente, agente virtual, interfaz de negocios. A fin de ilustrar las modificaciones que se deben realizar sobre el modelo original, para simular herencia mltiple, se ha utilizado el nombre iB para la interface. Sin embargo, en una situacin de aplicacin del esquema propuesto, se debera denominar a la clase B, por ejemplo como implB (implementacin de B) y a la interface iB con el nombre B, a fin de mantener la coherencia en los nombres de identificadores del modelo original. Si bien la implementacin requiere de varios artificios de codificacin, la complejidad de esta queda oculta para quien reutiliza la clase D, por lo tanto su tarea no se ve complicada, obteniendo como resultado el comportamiento esperable, de un lenguaje que soporte esquemas de herencia mltiple.

22

Proyecto Leonardo

Ao 4 Volumen 3 Nmero 1 Octubre de 2008

ISSN 1668-7523

No obstante lo anterior, la implementacin presentada es una solucin de compromiso a un problema para el que el lenguaje Java, no provee enunciados que permitan tratarlo en forma directa. Esto hace que lo propuesto tenga algunas desventajas: La clase de la cual se hereda no puede ser abstracta, ya que no sera posible crear el subobjeto, que ha permitido mantener el vnculo con el objeto de la clase de la cual se deriva, como se explica al comentar la implementacin del Listado 2. En el modelo original de la Figura 4, si se quiere manipular un objeto del tipo D, utilizando una referencia del tipo B, esto se puede hacer, y se aplica el comportamiento polimrfico. En cambio en el modelo de la Figura 5, esto se debe hacer empleando una referencia a la interface iB, para mantener las caractersticas de polimorfismo. El esquema de relaciones propuesto presenta dificultades para el crecimiento. Por ejemplo, para crear una nueva clase que herede de C y B, se debe modificar la clase B, para agregar el constructor que permite mantener la relacin inversa con la nueva clase. Este tipo de acoplamiento en un diseo, es una prctica que va en contra de los principios de la metodologa orientada a objetos. Tampoco se ha analizado, cmo se debera abordar el problema del acceso a miembros protegidos (atributos o mtodos), si estos aparecieran en el modelo original.

REFERENCIAS
Booch, G. (1994). Object Oriented Analysis and Design: With Applications. Addisson Wesley Budd, T. (2002). Introduction to Object-Oriented Programming, 3/E. Addison-Wesley Cardelli, L. Wegner, P. (1985). On Understanding Types, Data Abstractions, and Polymorphism. Computing Surveys. 17.4: 471-523 Cornell, G. Horstmann, C. S. (1996). Core Java. The Sun Press. Prentice Hall Eckel, B. (2000). Thinking in Java. Prentice Hall. Fowler, M. Scott, K. (2000). UML Distilled: A Brief Guide to the Standard Object Modeling Language 2/E. Addisson Wesley Gamma, E. Helm, R. Johnson, R. Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addisson Wesley Ghan Bir Singh. (1995). Rensselaer Polytechnic Institute at HGC. Single Versus Multiple Inheritance in Object Oriented Programming. Hartford, CT, USA Meyer, B. (2001). An Eiffel Tutorial, ISE Technical Report TR-EI-66/TU. Interactive Software Engineering Inc. Seidewitz, E. (1996). Controlling Inheritance. Journal of Object Oriented Programming. 8.8: 36-42 Stroustrup, B. (2000). The C++ Programming Language. Addisson Wesley

23

Das könnte Ihnen auch gefallen