Herencia múltiple
La herencia múltiple es una propiedad admitida por parte de los lenguajes de programación orientados a objetos , cuando una clase puede tener más de una superclase (clase principal directa), las interfaces admiten la herencia múltiple en muchos lenguajes de programación. Este concepto es una extensión de la "herencia simple (o única) " ( herencia única en inglés ), en la que una clase solo puede heredar de una superclase.
Los lenguajes que admiten la herencia múltiple incluyen: Io , Eiffel , C++ , Dylan , Python , algunas implementaciones de clases de JavaScript (por ejemplo , dojo .declare), Perl 6 , Curl , Common Lisp (gracias a CLOS ), OCaml , Tcl (gracias a Incremental Tcl ) [1] , así como Object REXX (debido al uso de clases mixin ).
Resumen
La herencia múltiple permite que una clase herede la funcionalidad de muchas otras clases, como una clase StudentMusicianque puede heredar de class Person, class Musiciany class Worker, que se puede abreviar como:
StudentMusician : Person, Musician, Worker.
La incertidumbre en la herencia múltiple, como en el ejemplo anterior, ocurre si, por ejemplo, una clase Musicianhereda de las clases Persony Worker, y la clase Worker, a su vez, hereda de Person; una situación similar se llama herencia en forma de diamante . Así, obtenemos las siguientes reglas:
Trabajador : Persona
Músico : Persona, Trabajador
EstudianteMúsico : Persona, Músico, Trabajador
Si el compilador está mirando la clase StudentMusician, entonces necesita saber si las características de las clases deben combinarse o separarse. Por ejemplo, sería lógico adjuntar la "Edad" (edad) de la clase Persona a la clase EstudianteMúsico. La edad de una persona no cambia si la consideras como Persona (persona), Obrero (trabajador) o Músico (músico). Sin embargo, sería bastante lógico separar la propiedad "Nombre" en las clases Persona y Músico si utilizan un nombre artístico diferente a su nombre real. Las opciones de unir y dividir son perfectamente válidas para cada uno de sus propios contextos, y solo el programador sabe qué opción es la correcta para la clase que se está diseñando.
Los lenguajes tienen varias formas de lidiar con tales problemas de herencia anidada, por ejemplo:
- Eiffel proporciona al programador la capacidad de combinar o dividir explícitamente los miembros heredados de las superclases. Eiffel fusionará automáticamente los elementos si tienen el mismo nombre e implementación. El autor de la clase tiene la capacidad de cambiar el nombre de los miembros heredados para separarlos. Además, Eiffel le permite volver a heredar explícitamente el archivo A: B, B.
- C++ requiere que el programador especifique qué miembro de la clase principal debe usarse, es decir, "Trabajador::Persona.Edad". C++ no admite la herencia explícitamente repetible porque no hay forma de determinar qué superclase usar (consulte la crítica ). C++ también permite la creación de una sola instancia de una clase múltiple debido al mecanismo de herencia virtual (por ejemplo, " Worker::Person" y " Musician::Person" se referirán al mismo objeto).
- Perl usa una lista de clases para heredar en ese orden. El compilador usa el primer método que encuentra cuando busca en profundidad la lista de superclases o usa la linealización C3 de la jerarquía de clases. Varias extensiones proporcionan esquemas de composición de clases alternativos.
- Python tiene soporte sintáctico para herencia múltiple, y el orden de las clases base está determinado por el algoritmo de linealización C3 [2] .
- El Common Lisp Object System proporciona un control total de los métodos de combinación por parte del programador, y si esto no es suficiente, entonces el Metaobject Protocol (Protocolo de metaobjetos) le brinda al programador la capacidad de modificar la herencia, la gestión dinámica , la implementación de clases y otros mecanismos internos sin temor a afectar la estabilidad del sistema.
- Logtalk admite tanto las interfaces como la implementación de la herencia múltiple al proporcionar una declaración de método de alias que admite tanto el cambio de nombre como el acceso a métodos que podrían no estar disponibles debido al mecanismo de resolución de conflictos.
- Curl solo permite clases que están explícitamente marcadas como reheredables. Las clases accesibles deben definir un constructor secundario para cada constructor de clase normal . Primero se llama al constructor normal, el constructor de la subclase inicializa el estado de la clase accesible y se llama al constructor secundario para todas las demás subclases.
- Ocaml elige la última definición coincidente en la lista de herencia de clases para determinar el método de implementación a utilizar en caso de incertidumbre. Para anular el comportamiento predeterminado, simplemente proporcione un método para llamar al definir la clase preferida.
- Tcl permite la existencia de múltiples clases principales: su secuencia afecta la resolución de los nombres de los miembros de la clase. [3]
- Delphi desde la versión 2007 le permite implementar parcialmente la herencia múltiple usando asistentes de clase (Class Helpers) .
Smalltalk , C# , Objective-C , Java , Nemerle y PHP no permiten la herencia múltiple, lo que evita muchas incertidumbres. Sin embargo, además de Smalltalk, permiten que las clases implementen múltiples interfaces . Además, PHP y Ruby le permiten emular la herencia múltiple mediante el uso de mixins (rasgos en PHP y mixins en Ruby), que, al igual que las interfaces, no son clases completas. La herencia múltiple de interfaces le permite ampliar capacidades limitadas.
Crítica
La herencia múltiple ha sido criticada por los siguientes problemas en algunos lenguajes, en particular C++:
- la ambigüedad semántica a menudo se representa colectivamente como el problema del diamante [4] .
- no hay posibilidad de herencia múltiple directa de una clase.
- el orden de herencia cambia la semántica de la clase. El constructor de la clase secundaria llama a los constructores de los padres inmediatos y estos, a su vez, llaman al constructor de los abuelos. Sin embargo, el objeto abuelo existe en una sola instancia y no se puede construir dos veces, por lo que funcionará llamar al constructor abuelo solo por el constructor de la primera clase padre en la lista de herencia.
La herencia múltiple en lenguajes con constructores de estilo C++/Java exacerba el problema de la herencia de constructores y las secuencias de constructores, creando así problemas de mantenibilidad y extensibilidad en esos lenguajes. Los objetos en relaciones de herencia con métodos de construcción significativamente diferentes son bastante difíciles de implementar dentro del paradigma de la secuencia del constructor.
Sin embargo, hay lenguajes que manejan estos tecnicismos (por ejemplo, Eiffel ).
Existe la opinión de que la herencia múltiple es un concepto erróneo, generado por un análisis y diseño erróneos. En particular, la siguiente opción de diseño es válida para el ejemplo anterior. La clase Persona incluye uno o más objetos de la clase Profesión. Las clases Estudiante y Músico heredan de Profesión. Así, EstudianteMúsico estará representado por un objeto de clase Persona que contiene objetos de clase Estudiante y Músico. Formalmente, la herencia múltiple puede someterse a ingeniería inversa mediante la introducción de una clase que sea una "metaclase" de las clases a partir de las cuales se producirá la herencia múltiple. En el ejemplo anterior, tal metaclase es Profesión - una profesión.
Notas
- ↑ Defensa de Tcl . Consultado el 2 de diciembre de 2009. Archivado desde el original el 22 de septiembre de 2010. (indefinido)
- ↑ David M. Beazley. Referencia esencial de Python . — 4ª edición. - Addison-Wesley Professional, 2009. - S. 119 -122. — 717 pág. — ISBN 978-0672329784 .
- ↑ Manual Tcl: clase . Consultado el 2 de diciembre de 2009. Archivado desde el original el 4 de abril de 2009. (indefinido)
- ↑ Rasgos: Unidades Componibles de Comportamiento . Consultado el 2 de diciembre de 2009. Archivado desde el original el 9 de agosto de 2017. (indefinido)
Enlaces
Literatura