¿Hay algún lenguaje OO compatible con un mecanismo para garantizar que un método anulado llame a la base?

12

Creo que esta podría ser una característica útil del idioma y me preguntaba si algún idioma ya lo admite.

La idea es si tienes:

class C
  virtual F
     statement1
     statement2

y

class D inherits C
  override F
     statement1
     statement2
     C.F()

Habría una palabra clave aplicada a CF () de tal manera que eliminar la última línea de código anterior causaría un error del compilador porque dice "Este método se puede anular, pero la implementación aquí debe ejecutarse sin importar qué".

Aaron Anodide
fuente

Respuestas:

14

Ellos si. Se llama modelo escandinavo de OO, se usa, por ejemplo, en Simula (el otro modelo de OO que está muy extendido y se da por sentado ahora, es el modelo estadounidense). En el modelo escandinavo, no está anulando, sino suministrando subcomportamiento.

en el método de Superclass foo:

some-code-before
INNER // this is the actual Simula keyword
some-code-after

en el método de la subclase foo:

some-code-in-subclass

Si llama al método 'instancias' de Superclass foo, solo some-code-beforey some-code-aftersucede ( INNERno hace nada), pero si llama a 'instancias' foo de la subclase, lo hace some-code-before, some-code-in-subclassy luego some-code-after.

herby
fuente
9

Ningún lenguaje que conozca impone el llamado al método anulado. De hecho, algunos lenguajes permiten métodos de anulación que no se pueden anular (como usar la newpalabra clave en C #). Sin embargo, hay dos formas de abordar esto.

El primero es crear un método que no se pueda modificar (p. Ej., Uno que no virtualtenga la finalpalabra clave en C # o uno que tenga la palabra clave en Java) que llame a uno que no se pueda reemplazar desde fuera de la clase (p. Ej protected., En C #, Java o C ++).

class C
  A
     statement1
     F
     statement3

  protected virtual F
     statement2

y

class D inherits C

  protected override F
     statement4
     C.F()

La anulación de clases Ces libre de anular Fy modificar su comportamiento, pero las personas que llaman desde fuera de la clase solo acceden a ella A.

Editar: como otros han señalado, esto se llama patrón de método de plantilla .

La segunda forma es usar un lenguaje que haga cumplir las precondiciones y postcondiciones especificadas en la clase base, como Eiffel o C # con contratos de código. No forzará la invocación de la clase base, pero el método anulado puede verse obligado a realizar las mismas declaraciones. El uso de aspectos también puede ayudar si el lenguaje permite que se hereden aspectos.

akton
fuente
2
Incluso puede hacer que el método se anule privateen C ++ :) Herb Sutter lo explica aquí en detalle.
fredoverflow
El patrón de plantilla solo tiene una desventaja: debe volver a implementarlo cada vez que profundiza en la jerarquía de herencia. El ejemplo con Simula es más elegante y aún permite el patrón de plantilla.
Pavel Voronin
7

No es realmente parte del lenguaje, pero el analizador de código estático FindBugs para Java tiene una anotación OverrideMustInvokeque un desarrollador puede agregar a un método, y que hará que FindBugs muestre un error si encuentra un método superior que no llama a la súper implementación . Incluso permite especificar si la llamada debe ser la primera o la última en el método de anulación.

Michael Borgwardt
fuente
6

Requerir llamar a un método de superclase es un antipatrón . Si no se aplica en tiempo de compilación, es propenso a errores, por lo que está buscando una construcción de lenguaje que lo compruebe.

Hay una forma compatible con todos los idiomas OO: el patrón de método de plantilla . Aquí, usted hace que el método de la superclase no sea reemplazable, y en él llama un método reemplazable. La subclase puede anular este método para agregar funcionalidad:

class super {
  public final void doSomething() {
    doSpecialthing();
    doMore();
  }
  public void doSpecialthing() {
  }
}

Dependiendo de la ubicación de la llamada al método anulado, incluso permite determinar el orden de ejecución, que con la súper llamada ordinaria es a voluntad del implementador de la subclase.

Ozan
fuente
1

El patrón más cercano que se me ocurre es el de los eventos auto suscritos. Es un poco engorroso y nada intuitivo para el codificador, pero logra el objetivo.

class C
{
    public void F()
    {
        ...
        OnF()
    }

    protected event OnF
}

class D : C
{
    public D()
    {
        base.OnF += this.F
    }

    private void F
    {
        ...
    }
}
Hand-E-Food
fuente
1
Este es un patrón de diseño bastante común, a veces con anulaciones tanto profesionales como posteriores, por ejemplo. ViewWillAppear (), ViewDidAppear (). Ideal cuando desea permitir que las subclases amplíen (en lugar de modificar) el comportamiento predeterminado.
Kris Van Bael
1

La máquina Lisp "sabores" permitió métodos con el tipo "antes" "después" y "alrededor" del método principal heredado.

ddyer
fuente
0

Si bien no es una mala idea en teoría, tiene el efecto secundario negativo de restringir mis opciones al implementar D. Por ejemplo, qué pasa si (por alguna razón insondable), es más conveniente llamar a la implementación de la superclase de Falgún otro método:

class D inherits C
    override F
        statement1
        statement2
        G()
    G
        statement3
        C.F()
        statement4

Bajo su escenario, imagino que el compilador marcaría la implementación de Fin D, a pesar de que llama (indirectamente) C.F().

Básicamente, lo que ha descrito es un posible mecanismo para ayudar al compilador a reconocer cuándo Cse viola un contrato de clases que heredan . Mi punto es que, si bien es una gran cosa, no debería venir a expensas de limitar cómo puedo implementar mi subclase.

Mac
fuente
1
-1: poder pensar en una situación en la que no querrías usar eso no es una respuesta a la pregunta "¿algún lenguaje lo permite"?
@GrahamLee: muy cierto. Mi punto era tratar de explicar una razón por la cual ningún lenguaje (que yo sepa) implementa tal característica. Creo que me quedé tan atrapado explicando eso, que olvidé mencionar por qué lo estaba explicando. -1 aceptó felizmente. :)
Mac