¿Por qué la herencia solo se define en tiempo de compilación?

7

Encontré esta declaración de "Patrones de diseño" de la cuadrilla de cuatro particularmente extraña; en algún contexto, los autores están comparando la herencia con la composición como mecanismos de reutilización [p. 19]:

  

"... no puede cambiar las implementaciones heredadas de las clases primarias   en tiempo de ejecución, porque la herencia se define en tiempo de compilación ".

Siguen diciendo de composición:

  

"La composición de objetos se define dinámicamente en tiempo de ejecución a través de objetos   adquiriendo referencias a otros objetos ".

No estoy seguro de por qué esta distinción de fase es importante. Estoy familiarizado con la compilación y la herencia, pero trabajo como desarrollador de JavaScript, así que tal vez me esté perdiendo algo fundamental.

    
pregunta gwg 21.01.2014 - 19:29

3 respuestas

8

Algunos idiomas son bastante estáticos, y solo permiten la especificación de la relación de herencia entre dos clases en el momento de la definición de esas clases. Para C ++, el tiempo de definición es prácticamente el mismo que el tiempo de compilación. (Es ligeramente diferente en Java y C #, pero no mucho.) Otros lenguajes permiten una reconfiguración mucho más dinámica de la relación de clases (y objetos similares a clases en Javascript) entre sí; algunos van tan lejos como para permitir que se modifique la clase de un objeto existente, o que se cambie la superclase de una clase. (Esto puede causar un caos lógico total , pero también puede modelar los desagradables del mundo real bastante bien).

Pero es importante contrastar esto con la composición, donde la relación entre un objeto y otro no está definida por su relación de clase (es decir, su tipo ) sino por las referencias que cada uno tiene relación con el otro. La composición general es un método muy poderoso y ubicuo para organizar los objetos: cuando un objeto necesita saber algo sobre otro, tiene una referencia a ese otro objeto e invoca métodos según sea necesario. Tan pronto como comiences a buscar este patrón super-fundamental , lo encontrarás absolutamente en todas partes; La única forma de evitarlo es poner todo en un objeto, ¡lo que sería enormemente tonto! (También hay una composición / agregación UML más estricta, pero no es de eso de lo que habla el libro de GoF).

Una de las cosas acerca de la relación de composición es que los objetos particulares no tienen que estar unidos entre sí. El patrón de los objetos concretos es muy flexible, incluso en lenguajes muy estáticos como C ++. (Hay una ventaja en tener cosas muy estáticas: es posible analizar el código más de cerca y, al menos potencialmente, emitir un mejor código con menos sobrecarga). Para resumir, Javascript, como con muchos otros lenguajes dinámicos, puede pretender que no utiliza la compilación en absoluto; solo pretensión, por supuesto, pero el modelo de lenguaje fundamental no requiere transformación a un formato intermedio fijo (por ejemplo, un "ejecutable binario en el disco"). La compilación que se realiza se realiza en tiempo de ejecución y se puede rehacer fácilmente si las cosas varían demasiado. (Lo fascinante es que se puede hacer un buen trabajo de compilación, incluso a partir de una base muy dinámica ...)

Algunos patrones GoF solo tienen sentido en el contexto de un lenguaje en el que las cosas son bastante estáticas. Está bien; solo significa que no todas las fuerzas que afectan el patrón están necesariamente listadas. Uno de los puntos clave sobre el estudio de patrones es que nos ayuda a ser conscientes de estas importantes diferencias y advertencias. (Otros patrones son más universales. Mantén tus ojos abiertos para ellos).

    
respondido por el Donal Fellows 21.01.2014 - 22:47
2

Consideremos un lenguaje fuertemente tipado como C #. Aquí definirías la herencia de esta manera:

class BaseClass
{
    public string SomeMethod()
    {
        return "Hello";
    }
}

class DerivedClass : BaseClass  // Inherits BaseClass
{
    public string AdditionalMethod()
    {
        return "World";
    }
}

Uso:

  var d = new DerivedClass();
  Console.WriteLine(d.SomeMethod()); // --> Hello
  Console.WriteLine(d.AdditionalMethod()); // --> World

La clase DerivedClass hereda el método SomeMethod de BaseClass . Este hecho se define en tiempo de compilación. A veces se usa el término "tiempo de diseño" en su lugar. El último término deja claro que la herencia ya está definida en el texto del código por el programador.

En contraste, veamos cómo funciona la composición

interface IReturnsString
{
    string GetString();
}

class ComposedClass
{                                                          
    private IReturnsString _a, _b;

    public ComposedClass(IReturnsString a, IReturnsString b) // Constructor
    {
        _a = a;
        _b = b;
    }

    public string SomeMethodA()
    {
        return _a.GetString();
    }

    public string SomeMethodB()
    {
        return _b.GetString();
    }

    public void ChangeBehavior()
    {
        _a = new ClassC(); // ClassC is supposed to implement IReturnsString.
    }
}

En el tiempo de ejecución, puede pasar diferentes implementaciones a la clase compuesta. La clase compuesta podría incluso elegir cambiar su comportamiento en tiempo de ejecución.

var x = new ComposedClass(new ClassA(), new ClassB());
Console.WriteLine(x.SomeMethodA()); // Prints something

x.ChangeBehavior();
Console.WriteLine(x.SomeMethodA()); // Prints something else

var y = new ComposedClass(new ClassD(), new ClassE());
// y behaves in a different way than x
    
respondido por el Olivier Jacot-Descombes 21.01.2014 - 22:37
1

En Javascript, aparentemente no hay mucha diferencia, porque no hay compilación. Por lo que leo, lo que comúnmente se llama herencia en JS es simplemente crear una cadena de prototipo entre diferentes objetos.
Pero en lenguajes como C #, puede crear una jerarquía de herencia en tiempo de compilación a través de clases que se heredan unas de otras, pero también puede cambiar el comportamiento de los objetos en tiempo de ejecución a través de patrones como el patrón de estrategia.

    
respondido por el Stefan Billiet 21.01.2014 - 19:42

Lea otras preguntas en las etiquetas