¿El método de anulación es siempre una violación del principio de sustitución de Liskov? [duplicar]

12

Al anular un método originalmente definido en la súper clase, por definición significa que este método hará cosas diferentes cuando se invoque en un objeto de la clase base o un objeto de la subclase.

¿Esto significa que los métodos de anulación siempre significan violar el LSP? ¿O hay casos en los que no?

    
pregunta Aviv Cohn 12.06.2014 - 12:36

5 respuestas

25

LSP prohíbe violar los contratos de un supertipo en un subtipo, no prohíbe cambiar el comportamiento de ningún Método (dentro de los límites de ese contrato).

Por ejemplo, supongamos que tiene una superclase Report , asociada con un determinado objeto Foo , con un método ToString() y subtipos HTMLReport , XMLReport y TextReport . Supongamos además que Report no es abstracto y que la implementación predeterminada de ToString() es devolver la cadena vacía. Ahora usted define un contrato de la siguiente manera:

  • ToString() entregará una cadena con una representación textual de Foo en un determinado formato de texto (y la cadena vacía si el formato no está definido hasta ahora).
  • ToString() no mutará el objeto Report
  • ToString() nunca lanzará una excepción

Por lo tanto, las subclases pueden anular fácilmente el método ToString , cada una implementando un comportamiento diferente, pero todas siguiendo perfectamente el LSP.

Por otro lado, si su contrato fuera

  • ToString() siempre devolverá la cadena vacía

entonces anularlo y devolver algo diferente violaría el LSP, pero tal contrato obviamente no tendría mucho sentido para cualquier escenario del mundo real.

    
respondido por el Doc Brown 12.06.2014 - 13:20
1

Primer caso simple: agregar almacenamiento en caché a una operación costosa. no hay diferencia funcional entre la función original y la nueva.

También se puede documentar que la función original haga algo y siempre que la función de anulación siga dentro de esa documentación, no hay problema (el núcleo del principio sustitución ). Recuerde que debe codificar contra la API, no la implementación.

    
respondido por el ratchet freak 12.06.2014 - 12:46
0

¿Por definición diferentes cosas? Whaddayoumean?

Liskov dice que no debes "ir en contra" del comportamiento de tu antepasado. No significa que no pueda ampliarlo o aumentarlo haciendo cosas además de ese comportamiento básico.

En su método anulado, llama a inherited para ejecutar el comportamiento del antepasado (en honor a Liskov) y luego puede hacer cosas además de eso.

No llamar a inherited en absoluto (*) sería una infracción de Liskov (a menos que usted vuelva a codificar la implementación de la base, que recomiendo encarecidamente). Actualizar @Ratchetfreak en realidad tiene un ejemplo bastante bueno en el que un descendiente solo llamaría heredado si no hubiera guardado un resultado en la caché para un cálculo "caro".

(*) tenga en cuenta que estoy acostumbrado a un idioma al que puede llamar heredado independientemente de si el antepasado o el método del antepasado están marcados como abstractos. El compilador saca la llamada.

    
respondido por el Marjan Venema 12.06.2014 - 12:41
0

El Principio de Sustitución de Liskov solo dice que las subclases no deben violar propiedades disponibles del supertipo. Las propiedades demostrables son básicamente las firmas de tipo de los miembros. Entonces, si se declara que un método en una superclase devuelve un entero, entonces no se debe sobrescribir en una subclase para devolver una cadena.

Los sistemas tipográficos en lenguajes OO modernos de tipo estático generalmente evitan que esto suceda, por lo que como desarrollador ordinario de dicho código C # no debería preocuparse demasiado por romper el LSP (aunque creo que hay algunas debilidades en el sistema de tipos En cuanto a Arrays que le permite romper LSP).

El principio a menudo se entiende más ampliamente (y vagamente) que las subclases no deben "romper las expectativas", lo que definitivamente es un buen principio de diseño, pero no realmente lo que dice el LSP.

    
respondido por el JacquesB 29.06.2015 - 15:41
0

Como ejemplo, si tiene una clase con instancias que representan un elemento de la interfaz de usuario, y esa clase tiene un método de "dibujo", entonces la especificación del método de "dibujo" no dirá exactamente lo que se dibujará. En su lugar, dirá que "el elemento de la interfaz de usuario se dibujará de manera apropiada para informar al usuario sobre todo el estado del elemento de la interfaz de usuario que es importante para el usuario, de una manera intuitiva y de aspecto agradable". Si subclasifica ese elemento de la interfaz de usuario, el método de dibujo de subclase debería y probablemente actuará exactamente de acuerdo con la especificación.

Como principio general, si el principio de sustitución de Liskov hiciera imposible la subclase, sería bastante estúpido, y los principios bastante estúpidos no obtienen nombres y entradas de Wikipedia y se olvidan rápidamente :-)

(BTW. Es un arte que a menudo se olvida de escribir una especificación adecuada para un método que se supone que se invalida en una subclase, porque cualquier comportamiento existente no describe la especificación, sólo un caso especial de ello).

    
respondido por el gnasher729 29.06.2015 - 16:58

Lea otras preguntas en las etiquetas