Articulo de referencia

Herencia (programación orientada a objetos)

En la programación orientada a objetos , la herencia es el mecanismo para basar un objeto o clase en otro objeto ( herencia basada en prototipos ) o clase ( herencia basada en c...

En la programación orientada a objetos , la herencia es el mecanismo para basar un objeto o clase en otro objeto ( herencia basada en prototipos ) o clase ( herencia basada en clases ), manteniendo una implementación similar . También se define como la derivación de nuevas clases ( subclases ) a partir de las existentes, como la superclase o la clase base , y su posterior formación en una jerarquía de clases. En la mayoría de los lenguajes orientados a objetos basados ​​en clases, como C++ , un objeto creado mediante herencia, un "objeto hijo", adquiere todas las propiedades y comportamientos del "objeto padre", con la excepción de: constructores , destructores, operadores sobrecargados y funciones amigas de la clase base. La herencia permite a los programadores crear clases que se construyen sobre clases existentes, [ 1 ] especificar una nueva implementación manteniendo los mismos comportamientos ( realizando una interfaz ), reutilizar código y extender de forma independiente el software original mediante clases públicas e interfaces . Las relaciones de objetos o clases a través de la herencia dan lugar a un grafo dirigido acíclico .

Una clase heredada se denomina subclase de su clase padre o superclase. El término herencia se usa de forma general tanto para la programación basada en clases como para la basada en prototipos, pero en un sentido estricto se reserva para la programación basada en clases (una clase hereda de otra), mientras que la técnica correspondiente en la programación basada en prototipos se denomina delegación (un objeto delega en otro). Los patrones de herencia que modifican clases pueden predefinirse según parámetros sencillos de la interfaz de red, de modo que se preserve la compatibilidad entre lenguajes. [ 2 ] [ 3 ]

La herencia no debe confundirse con el subtipado . [ 4 ] [ 5 ] En algunos lenguajes, generalmente lenguajes de programación orientada a objetos (POO) basados ​​en clases y con tipado estático, como C++ , C# , Java y Scala , la herencia y el subtipado coinciden, mientras que en otros difieren. En general, el subtipado establece una relación "es un" , mientras que la herencia solo reutiliza la implementación y establece una relación sintáctica, no necesariamente una relación semántica (la herencia no garantiza el subtipado de comportamiento). Para distinguir estos conceptos, al subtipado a veces se le denomina herencia de interfaz (sin reconocer que la especialización de las variables de tipo también induce una relación de subtipado), mientras que la herencia, tal como se define aquí, se conoce como herencia de implementación o herencia de código . [ 6 ] Aun así, la herencia es un mecanismo comúnmente utilizado para establecer relaciones de subtipo. [ 7 ]

La herencia se contrapone a la composición de objetos , donde un objeto contiene a otro (o los objetos de una clase contienen objetos de otra clase); véase composición frente a herencia . A diferencia de la relación " es un " del subtipado , la composición implementa una relación " tiene un ".

Desde el punto de vista matemático, la herencia en cualquier sistema de clases induce un orden parcial estricto en el conjunto de clases de ese sistema.

Historia

En 1966, Tony Hoare presentó algunas observaciones sobre los registros, y en particular, la idea de subclases de registros, tipos de registros con propiedades comunes pero diferenciados por una etiqueta variante y con campos privados para la variante. [ 8 ] Influenciados por esto, en 1967 Ole-Johan Dahl y Kristen Nygaard presentaron un diseño que permitía especificar objetos que pertenecían a diferentes clases pero tenían propiedades comunes. Las propiedades comunes se recopilaban en una superclase, y cada superclase podía a su vez tener potencialmente una superclase. Los valores de una subclase eran así objetos compuestos, que consistían en un número de partes de prefijo pertenecientes a varias superclases, más una parte principal perteneciente a la subclase. Todas estas partes se concatenaban. [ 9 ] Los atributos de un objeto compuesto serían accesibles mediante la notación de punto. Esta idea se adoptó por primera vez en el lenguaje de programación Simula 67. [ 10 ] La idea luego se extendió a Smalltalk , C++ , Java , Python y muchos otros lenguajes.

Tipos

Herencia simple
Herencia múltiple

Existen varios tipos de herencia, basados ​​en el paradigma y el lenguaje específico. [ 11 ]

Herencia simple
donde las subclases heredan las características de una superclase. Una clase adquiere las propiedades de otra clase.
Herencia múltiple
donde una clase puede tener más de una superclase y heredar características de todas las clases padre.

"  Se creía que la herencia múltiple... era muy difícil de implementar de manera eficiente. Por ejemplo, en un resumen de C++ en su libro sobre Objective C , Brad Cox afirmó que agregar herencia múltiple a C++ era imposible. Por lo tanto, la herencia múltiple parecía un desafío mayor. Dado que ya había considerado la herencia múltiple en 1982 y encontré una técnica de implementación simple y eficiente en 1984, no pude resistir el desafío. Sospecho que este es el único caso en el que la moda afectó la secuencia de eventos." [ 12 ]

herencia multinivel
donde una subclase hereda de otra subclase. No es raro que una clase derive de otra clase derivada, como se muestra en la figura "Herencia multinivel".
herencia multinivel
La clase A sirve como clase base para la clase derivada B , que a su vez sirve como clase base para la clase derivada C. La clase B se conoce como clase base intermedia porque proporciona un vínculo para la herencia entre A y C. La cadena ABC se conoce como ruta de herencia .
Una clase derivada con herencia multinivel se declara de la siguiente manera:
// Implementación en lenguaje C++ class A { ... }; // Clase base class B : public A { ... }; // B derivada de A class C : public B { ... }; // C derivada de B
Este proceso puede extenderse a cualquier número de niveles.
Herencia jerárquica
Aquí, una clase sirve como superclase (clase base) para más de una subclase. Por ejemplo, una clase padre, A, puede tener dos subclases, B y C. Tanto B como C son la clase padre de A, pero B y C son dos subclases distintas.
Herencia híbrida
La herencia híbrida se produce cuando se combinan dos o más de los tipos de herencia mencionados anteriormente. Un ejemplo de esto es cuando una clase A tiene una subclase B, que a su vez tiene dos subclases, C y D. Esto representa una combinación de herencia multinivel y herencia jerárquica.

Subclases y superclases

Las subclases , clases derivadas , clases herederas o clases hijas son clases derivadas modulares que heredan una o más entidades del lenguaje de una o más clases (denominadas superclases , clases base o clases padre ). La semántica de la herencia de clases varía según el lenguaje, pero generalmente la subclase hereda automáticamente las variables de instancia y las funciones miembro de sus superclases.

En C++, la forma general de definir una clase derivada es: [ 13 ]

clase Subclase : visibilidad Superclase { // miembros de la subclase };

Los dos puntos indican que la subclase hereda de la superclase. El modificador de visibilidad es opcional y, si está presente, puede ser privado o público . La visibilidad predeterminada (si no hay modificador) es privada . La visibilidad especifica si las características de la clase base se derivan de forma privada o pública .

Tenga en cuenta que en otros lenguajes como Java y C#, no existe un modificador de visibilidad para la herencia:

// Sin modificador de visibilidad // Equivalente a public SuperClass en C++ class SubClass extends SuperClass { // miembros de la subclase }

Algunos lenguajes también admiten la herencia de otras construcciones. Por ejemplo, en Eiffel , los contratos que definen la especificación de una clase también son heredados por los herederos. La superclase establece una interfaz común y una funcionalidad fundamental, que las subclases especializadas pueden heredar, modificar y complementar. El software heredado por una subclase se considera reutilizado en la subclase. Una referencia a una instancia de una clase puede en realidad referirse a una de sus subclases. La clase real del objeto al que se hace referencia es imposible de predecir en tiempo de compilación . Se utiliza una interfaz uniforme para invocar las funciones miembro de objetos de varias clases diferentes. Las subclases pueden reemplazar las funciones de la superclase con funciones completamente nuevas que deben compartir la misma firma de método .

Clases no subclasificables

En algunos lenguajes, una clase puede declararse como no heredable añadiendo ciertos modificadores a su declaración. Ejemplos de ello son la finalpalabra clave `class` en Java y C++11 en adelante, o la sealedpalabra clave `class` en C#. Estos modificadores se añaden a la declaración de la clase antes de la classpalabra clave y la declaración del identificador de clase. Estas clases no heredables restringen la reutilización , especialmente cuando los desarrolladores solo tienen acceso a binarios precompilados y no al código fuente .

Una clase que no admite subclases no tiene subclases, por lo que se puede deducir fácilmente en tiempo de compilación que las referencias o punteros a objetos de esa clase hacen referencia a instancias de esa clase y no a instancias de subclases (ya que no existen) ni a instancias de superclases ( la conversión ascendente de un tipo de referencia viola el sistema de tipos). Dado que el tipo exacto del objeto al que se hace referencia se conoce antes de la ejecución, se puede utilizar el enlace temprano (también llamado despacho estático ) en lugar del enlace tardío (también llamado despacho dinámico ), que requiere una o más búsquedas en la tabla de métodos virtuales, dependiendo de si el lenguaje de programación que se esté utilizando admite herencia múltiple o solo herencia simple .

Métodos no anulables

Así como las clases pueden no ser subclasables, las declaraciones de métodos pueden contener modificadores que impiden que el método sea sobrescrito (es decir, reemplazado por una nueva función con el mismo nombre y firma de tipo en una subclase). Un método privado no es sobrescrito simplemente porque no es accesible por clases distintas de la clase a la que pertenece (aunque esto no se aplica a C++). Un finalmétodo en Java, un sealedmétodo en C# o una frozencaracterística en Eiffel no pueden ser sobrescritos.

Métodos virtuales

Si un método de una superclase es virtual , las invocaciones de dicho método se despacharán dinámicamente . Algunos lenguajes requieren que el método se declare específicamente como virtual (por ejemplo, C++), mientras que en otros, todos los métodos son virtuales (por ejemplo, Java). La invocación de un método no virtual siempre se despachará estáticamente (es decir, la dirección de la llamada a la función se determina en tiempo de compilación). El despacho estático es más rápido que el dinámico y permite optimizaciones como la expansión en línea .

Visibilidad de los miembros heredados

La siguiente tabla muestra qué variables y funciones se heredan dependiendo de la visibilidad dada al derivar la clase, utilizando la terminología establecida por C++. [ 14 ]

Aplicaciones

La herencia se utiliza para correlacionar dos o más clases entre sí.

Primordial

Ilustración de la anulación del método

Muchos lenguajes de programación orientados a objetos permiten que una clase u objeto reemplace la implementación de un aspecto —normalmente un comportamiento— que ha heredado. Este proceso se llama sobreescritura . La sobreescritura introduce una complicación: ¿qué versión del comportamiento utiliza una instancia de la clase heredada : la que forma parte de su propia clase o la de la clase padre (base)? La respuesta varía entre los lenguajes de programación, y algunos lenguajes ofrecen la posibilidad de indicar que un comportamiento particular no debe ser sobrescrito y debe comportarse como lo define la clase base. Por ejemplo, en C#, el método o propiedad base solo puede ser sobrescrito en una subclase si está marcado con el modificador virtual, abstract u override, mientras que en lenguajes de programación como Java, se pueden llamar diferentes métodos para sobrescribir otros métodos. [ 15 ] Una alternativa a la sobreescritura es ocultar el código heredado.

Reutilización de código

La herencia de implementación es el mecanismo mediante el cual una subclase reutiliza código de una clase base. Por defecto, la subclase conserva todas las operaciones de la clase base, pero puede sobrescribir algunas o todas, reemplazando la implementación de la clase base con la suya propia.

En el siguiente ejemplo, las subclases SquareSumComputersobrescriben CubeSumComputerel transform()método de la clase base SumComputer. La clase base comprende operaciones para calcular la suma de los cuadrados de dos números enteros. La subclase reutiliza toda la funcionalidad de la clase base, con la excepción de la operación que transforma un número en su cuadrado, reemplazándola por una operación que transforma un número en su cuadrado y cubo , respectivamente. Por lo tanto, las subclases calculan la suma de los cuadrados y cubos de dos números enteros.

A continuación se muestra un ejemplo de Java.

import java.util.List ; import java.util.stream.Collectors ; import java.util.stream.IntStream ;clase abstracta SumComputer { entero privado a ; entero privado b ;public SumComputer ( int a , int b ) { this . a = a ; this . b = b ; }// Método abstracto que deben implementar las subclases public abstract int transform ( int x );public List < Integer > inputs () { return IntStream . rangeClosed ( a , b ) . boxed . collect ( Collectors . toList ()); }public int compute () { return inputs (). stream (). . mapToInt ( this :: transform ) . sum (); } }clase SquareSumComputer extiende SumComputer { public SquareSumComputer ( int a , int b ) { super ( a , b ); }@Override public int transform ( int x ) { return x * x ; } }clase CubeSumComputer extiende SumComputer { public CubeSumComputer ( int a , int b ) { super ( a , b ); }@Override public int transform ( int x ) { return x * x * x ; } }

En la mayoría de los ámbitos, la herencia de clases con el único propósito de reutilizar código ha caído en desuso. La principal preocupación es que la herencia de implementación no garantiza la sustituibilidad polimórfica : una instancia de la clase que reutiliza no necesariamente puede sustituirse por una instancia de la clase heredada. Una técnica alternativa, la delegación explícita , requiere más esfuerzo de programación, pero evita el problema de la sustituibilidad. En C++, la herencia privada puede utilizarse como una forma de herencia de implementación sin sustituibilidad. Mientras que la herencia pública representa una relación de "es un" y la delegación una relación de "tiene un", la herencia privada (y protegida) puede entenderse como una relación de "se implementa en términos de". [ 16 ]

Otro uso frecuente de la herencia es garantizar que las clases mantengan una interfaz común; es decir, que implementen los mismos métodos. La clase padre puede ser una combinación de operaciones implementadas y operaciones que se implementarán en las clases hijas. A menudo, no hay cambio de interfaz entre el supertipo y el subtipo: la clase hija implementa el comportamiento descrito en lugar de su clase padre. [ 17 ]

Herencia frente a subtipificación

La herencia es similar a la subtipificación , pero distinta de ella . [ 4 ] La subtipificación permite sustituir un tipo dado por otro tipo o abstracción y se dice que establece una relación de tipo "es un" entre el subtipo y alguna abstracción existente, ya sea implícita o explícitamente, dependiendo del soporte del lenguaje. La relación puede expresarse explícitamente mediante la herencia en lenguajes que la admiten como mecanismo de subtipificación. Por ejemplo, el siguiente código C++ establece una relación de herencia explícita entre las clases B y A , donde B es tanto una subclase como un subtipo de A y puede usarse como A dondequiera que se especifique B (mediante una referencia, un puntero o el objeto mismo).

clase A { público : void métodoDeA () const { // ... } };clase B : público A { público : void métodoDeB () const { // ... } };void functionOnA ( const A & a ) { a . methodOfA (); }int main () { B b ; functionOnA ( b ); // b puede sustituirse por una A. }

En los lenguajes de programación que no admiten la herencia como mecanismo de subtipado , la relación entre una clase base y una clase derivada es solo una relación entre implementaciones (un mecanismo para la reutilización de código), en comparación con una relación entre tipos . La herencia, incluso en los lenguajes de programación que la admiten como mecanismo de subtipado, no implica necesariamente un subtipado de comportamiento . Es totalmente posible derivar una clase cuyo objeto se comportará incorrectamente cuando se use en un contexto donde se espera la clase padre; véase el principio de sustitución de Liskov . [ 18 ] (Compárese connotación/denotación .) En algunos lenguajes de POO, las nociones de reutilización de código y subtipado coinciden porque la única forma de declarar un subtipo es definir una nueva clase que herede la implementación de otra.

Restricciones de diseño

El uso extensivo de la herencia en el diseño de un programa impone ciertas restricciones.

Por ejemplo, consideremos una clase Personque contiene el nombre, la fecha de nacimiento, la dirección y el número de teléfono de una persona. Podemos definir una subclase de Persona llamada Studentque contenga el promedio de calificaciones y las asignaturas cursadas por la persona, y otra subclase de Persona llamada Empleado que contenga el puesto de trabajo, la empresa y el salario de la persona.

Al definir esta jerarquía de herencia, ya hemos definido ciertas restricciones, no todas deseables:

  • Unicidad : Con la herencia simple, una subclase solo puede heredar de una superclase. Siguiendo el ejemplo anterior, un Personobjeto puede ser de tipo Studento de tipo Employee, pero no de ambos. La herencia múltiple resuelve parcialmente este problema, ya que permite definir una StudentEmployeeclase que herede tanto de como Studentde Employee. Sin embargo, en la mayoría de las implementaciones, solo puede heredar de cada superclase una vez, por lo que no admite casos en los que un estudiante tenga dos trabajos o asista a dos instituciones. El modelo de herencia disponible en Eiffel lo hace posible gracias a la compatibilidad con la herencia repetida .
  • Estático : La jerarquía de herencia de un objeto se fija en el momento de su instanciación , cuando se selecciona su tipo, y no cambia con el tiempo. Por ejemplo, el grafo de herencia no permite que un Studentobjeto se convierta en otro Employeeobjeto conservando el estado de su Personsuperclase. (Sin embargo, este comportamiento se puede lograr con el patrón decorador ). Algunos han criticado la herencia, argumentando que obliga a los desarrolladores a ceñirse a sus estándares de diseño originales. [ 19 ]
  • Visibilidad : Siempre que el código cliente tenga acceso a un objeto, generalmente tendrá acceso a todos los datos de la superclase del objeto. Incluso si la superclase no se ha declarado pública, el cliente aún puede convertir el objeto al tipo de su superclase. Por ejemplo, no hay forma de darle a una función un puntero al Studentpromedio de calificaciones y al expediente académico de un estudiante sin darle también acceso a todos los datos personales almacenados en la Personsuperclase del estudiante. Muchos lenguajes modernos, incluidos C++ y Java, proporcionan un modificador de acceso "protegido" que permite a las subclases acceder a los datos, pero no permite que ningún código fuera de la cadena de herencia acceda a ellos.

El principio de reutilización compuesta es una alternativa a la herencia. Esta técnica permite el polimorfismo y la reutilización de código al separar los comportamientos de la jerarquía de clases principal e incluir clases de comportamiento específicas según sea necesario en cualquier clase del dominio de negocio. Este enfoque evita la naturaleza estática de una jerarquía de clases al permitir modificaciones de comportamiento en tiempo de ejecución y permite que una clase implemente comportamientos de forma flexible, en lugar de estar restringida a los comportamientos de sus clases ancestras.

Problemas y alternativas

La herencia de implementación ha sido objeto de controversia entre programadores y teóricos de la programación orientada a objetos desde al menos la década de 1990. Entre los críticos se encuentran los autores de Patrones de Diseño , quienes abogan por la herencia de interfaz y prefieren la composición a la herencia . Por ejemplo, el patrón decorador (como se mencionó anteriormente ) se propuso para superar la naturaleza estática de la herencia entre clases. Como solución más fundamental al mismo problema, la programación orientada a roles introduce una relación distinta, denominada "play-by" , que combina propiedades de herencia y composición en un nuevo concepto.

Según Allen Holub , el principal problema de la herencia de implementación es que introduce un acoplamiento innecesario en forma del "problema de la clase base frágil" : [ 6 ] las modificaciones a la implementación de la clase base pueden causar cambios de comportamiento inadvertidos en las subclases. El uso de interfaces evita este problema porque no se comparte ninguna implementación, solo la API. [ 19 ] Otra forma de expresarlo es que "la herencia rompe la encapsulación ". [ 20 ] El problema se manifiesta claramente en sistemas abiertos orientados a objetos, como los frameworks , donde se espera que el código del cliente herede de clases proporcionadas por el sistema y luego sustituya a las clases del sistema en sus algoritmos. [ 6 ]

Según se informa, el inventor de Java, James Gosling, se ha manifestado en contra de la herencia de implementación, afirmando que no la incluiría si rediseñara Java. [ 19 ] Los diseños de lenguaje que desacoplan la herencia del subtipo (herencia de interfaz) aparecieron ya en 1990; [ 21 ] un ejemplo moderno de esto es el lenguaje de programación Go .

La herencia compleja, o la herencia utilizada en un diseño insuficientemente maduro, puede provocar el problema del yo-yo . Cuando la herencia se utilizaba como enfoque principal para estructurar programas a finales de la década de 1990, los desarrolladores tendían a dividir el código en más capas de herencia a medida que crecía la funcionalidad del sistema. Si un equipo de desarrollo combinaba múltiples capas de herencia con el principio de responsabilidad única, esto resultaba en muchas capas de código muy delgadas, muchas de las cuales consistían en solo 1 o 2 líneas de código real. Demasiadas capas dificultan considerablemente la depuración, ya que resulta complicado determinar qué capa necesita ser depurada.

Otro problema de la herencia es que las subclases deben definirse en el código, lo que significa que los usuarios del programa no pueden añadir nuevas subclases en tiempo de ejecución. Otros patrones de diseño (como Entidad-componente-sistema ) permiten a los usuarios definir variaciones de una entidad en tiempo de ejecución.

Véase también

Referencias

  1. Johnson, Ralph (26 de agosto de 1991). "Diseño de clases reutilizables" (PDF) . www.cse.msu.edu .
  2. Madsen, OL (1989). «Clases virtuales: Un mecanismo poderoso en la programación orientada a objetos». Actas de la conferencia sobre sistemas, lenguajes y aplicaciones de programación orientada a objetos - OOPSLA '89 . pp. 397–406 . doi : 10.1145/74877.74919 . ISBN  0897913337. S2CID 1104130 . 
  3. Davies, Turk (2021). Métodos avanzados y aprendizaje profundo en visión por computadora . Elsevier Science. pp. 179–342 . 
  4. 1 2 Cook, William R.; Hill, Walter; Canning, Peter S. (1990). La herencia no es subtipado . Actas del 17.º Simposio ACM SIGPLAN-SIGACT sobre Principios de Lenguajes de Programación (POPL). págs. 125–135 . CiteSeerX 10.1.1.102.8635 . doi : 10.1145/96709.96721 . ISBN   0-89791-343-4.
  5. Cardelli, Luca (1993). Programación tipada (Informe técnico). Digital Equipment Corporation . págs. 32–33. Informe de investigación SRC 45. 
  6. 1 2 3 Mikhajlov, Leonid; Sekerinski, Emil (1998). Un estudio del problema de la clase base frágil (PDF) . Actas de la 12.ª Conferencia Europea sobre Programación Orientada a Objetos (ECOOP). Lecture Notes in Computer Science. Vol. 1445. Springer. pp. 355–382 . doi : 10.1007/BFb0054099 . ISBN   978-3-540-64737-9Archivado del original (PDF) el 13 de agosto de 2017. Consultado el 28 de agosto de 2015 .
  7. Tempero, Ewan; Yang, Hong Yul; Noble, James (2013). Lo que hacen los programadores con la herencia en Java (PDF) . ECOOP 2013 – Programación Orientada a Objetos. Lecture Notes in Computer Science. Vol. 7920. Springer. pp. 577–601 . doi : 10.1007/978-3-642-39038-8_24 . ISBN   978-3-642-39038-8.
  8. Hoare, CAR (1966). Manejo de registros (PDF) (Informe técnico). págs. 15–16 . 
  9. Dahl, Ole-Johan ; Nygaard, Kristen (mayo de 1967). Declaraciones de clases y subclases (PDF) . Conferencia de trabajo de la IFIP sobre lenguajes de simulación. Oslo: Centro Noruego de Computación.
  10. Dahl, Ole-Johan (2004). "El nacimiento de la orientación a objetos: los lenguajes Simula" (PDF) . De la orientación a objetos a los métodos formales . Lecture Notes in Computer Science. Vol. 2635. pp. 15–25 . doi : 10.1007/978-3-540-39993-3_3 . ISBN   978-3-540-21366-6.
  11. "Herencia de C++" . www.cs.nmsu.edu . Archivado del original el 24 de septiembre de 2023. Consultado el 16 de mayo de 2018 .
  12. Stroustrup, Bjarne (1994). El diseño y la evolución de C++ . Pearson. pág. 417. ISBN  9780135229477.
  13. Schildt, Herbert (2003). The complete reference C++ . Tata McGraw Hill. p. 417 . ISBN  978-0-07-053246-5.
  14. Balagurusamy, E. (2010). Programación orientada a objetos con C++ . Tata McGraw Hill. pág. 213. ISBN  978-0-07-066907-9.
  15. anulación(Referencia de C#)
  16. "GotW #60: Diseño de clases seguro ante excepciones, parte 2: Herencia" . Gotw.ca. Consultado el 15 de agosto de 2012 .
  17. ^ Venugopal, KR; Buyya, Rajkumar (2013). Dominar C++ . Tata McGraw Hill Education Private Limited. pag. 609.ISBN  9781259029943.
  18. Mitchell, John (2002). "10 "Conceptos en lenguajes orientados a objetos"Conceptos de lenguajes de programación . Cambridge University Press. pág. 287. ISBN  978-0-521-78098-8.
  19. 1 2 3 Holub, Allen (1 de agosto de 2003). "Por qué extender es malo" . Archivado del original el 24 de febrero de 2019. Recuperado el 10 de marzo de 2015 .
  20. Seiter, Linda M.; Palsberg, Jens; Lieberherr, Karl J. (1996). "Evolución del comportamiento de los objetos mediante relaciones de contexto" . ACM SIGSOFT Software Engineering Notes . 21 (6): 46. CiteSeerX 10.1.1.36.5053 . doi : 10.1145/250707.239108 . 
  21. America, Pierre (1991). Diseño de un lenguaje de programación orientado a objetos con subtipado conductual . REX School/Workshop on the Foundations of Object-Oriented Languages. Lecture Notes in Computer Science. Vol. 489. pp. 60–90 . doi : 10.1007/BFb0019440 . ISBN   978-3-540-53931-5.

Lecturas adicionales

Obtenido de " https://en.wikipedia.org/w/index.php?title=Inheritance_(object-oriented_programming)&oldid=1357699289#Subclasses_and_superclasses "