uncle solid segregation principle liskov examples dependency bob design-principles solid-principles lsp

design principles - solid - Principio de sustitución de Liskov: ¿no hay métodos de anulación/virtuales?



solid principles examples (6)

Mi comprensión del principio de sustitución de Liskov es que alguna propiedad de la clase base que es verdadera o algún comportamiento implementado de la clase base, también debe ser cierto para la clase derivada.

Supongo que esto significaría que cuando se define un método en una clase base, nunca se debe sobrescribir en la clase derivada, ya que sustituir la clase base en lugar de la clase derivada daría resultados diferentes. Supongo que esto también significa que tener métodos virtuales (no puros) es algo malo.

Creo que podría tener un entendimiento erróneo del principio. Si no lo hago, no entiendo por qué este principio es una buena práctica. ¿Alguien me puede explicar esto? Gracias


Anulación rompe el Principio de Sustitución de Liskov si cambia cualquier comportamiento definido por un método base. Lo que significa que:

  1. La precondición más débil para un método secundario no debería ser más fuerte que para el método base.
  2. Una poscondición para el método secundario implica una condición posterior para el método principal. Donde una condición posterior está formada por: a) todos los efectos secundarios causados ​​por la ejecución de un método yb) el tipo y el valor de una expresión devuelta.

A partir de estos dos requisitos, puede implicar que cualquier nueva funcionalidad en un método secundario que no afecte a lo que se espera de un supermétodo no infringe el principio. Estas condiciones le permiten usar una instancia de subclase donde se requiere una instancia de superclase.

Si no se obedecen estas reglas, una clase viola LSP. Un ejemplo clásico es la siguiente jerarquía: Point(x,y) clase Point(x,y) , clase ColoredPoint(x,y,color) que extiende Point(x,y) y método reemplazado equals(obj) en ColoredPoint que refleja igualdad por color. Ahora, si uno tiene una instancia de Set<Point> , puede suponer que dos puntos con las mismas coordenadas son iguales en este conjunto. Lo cual no es el caso con el método reemplazado equals y, en general, simplemente no hay forma de extender una clase instanciable y agregar un aspecto utilizado en el método equals sin romper el LSP.

Por lo tanto, cada vez que rompe este principio, introduce implícitamente un posible error que revela cuando no se cumple con la invariante para una clase principal que el código espera. Sin embargo, en el mundo real a menudo no existe una solución de diseño obvia que no viole el LSP, por lo que se puede usar, por ejemplo, la anotación de clase @ViolatesLSP para advertir a un cliente que no es seguro usar instancias de clase en un conjunto polimórfico o en cualquier otro tipo de casos que se basan en el principio de sustitución Liskov.


Creo que eres literalmente correcto en la forma en que describes el principio y solo reemplazas los métodos puros virtuales o abstractos para asegurarte de no violarlo.

Sin embargo, si observa el principio desde el punto de vista de un cliente, es decir, un método que toma como referencia la clase base. Si este método no puede decir (y ciertamente no intenta y no necesita averiguar) la clase de cualquier instancia que se transfiere, entonces tampoco está violando el principio. Por lo tanto, puede no importar que anule un método de clase base (algunos tipos de decoradores pueden hacer esto, llamando al método de la clase base en el proceso).

Si un cliente parece necesitar averiguar la clase de una instancia aprobada, entonces se encontrará con una pesadilla de mantenimiento, ya que en realidad debería agregar nuevas clases como parte de su esfuerzo de mantenimiento, no modificando una rutina existente. (ver también OCP )


El principio de sustitución de Liskov permite totalmente el uso de métodos de sustitución de subclases en la clase base.

Esto podría simplificarlo demasiado, pero lo recuerdo como "una subclase no debería requerir nada más y prometer nada menos"

Si un cliente usa una superclase ABC con un método something(int i) , entonces el cliente debería poder sustituir cualquier subclase de ABC sin problemas. En lugar de pensar en esto en términos de tipos de variables, tal vez piense en términos de condiciones previas y postcondiciones.

Si nuestro método something() en la clase base ABC anterior tiene una condición previa relajada que permite cualquier número entero, entonces todas las subclases de ABC también deben permitir cualquier número entero. Una subclase GreenABC no puede agregar una condición previa adicional al método something() que requiere que el parámetro sea un entero positivo . Esto violaría el Principio de Sustitución de Liskov (es decir, requeriría más). Por lo tanto, si un cliente utiliza la subclase BlueABC y pasa enteros negativos a something() el cliente no se romperá si necesitamos cambiar a GreenABC .

A la inversa, si el método base clase ABC something() tiene una condición posterior, como garantizar que nunca devolverá un valor de cero, entonces todas las subclases también deben obedecer esa misma condición o violan el Principio de sustitución de Liskov (es decir, prometen menos) .

Espero que esto ayude.


Hay un ejemplo popular que dice que si nada como un pato, al curandero le gusta un pato pero requiere baterías, entonces rompe el Principio de Sustitución de Liskov.

En pocas palabras, tienes una clase base Duck que está siendo utilizada por alguien. A continuación, agrega la jerarquía mediante la introducción de PlasticDuck con los mismos comportamientos anulados (como natación, graznido, etc.) a partir de un pato, pero requiere baterías para simular esos comportamientos. Esto significa esencialmente que está introduciendo una precondición adicional al comportamiento de la Subclase para requerir que las baterías hagan el mismo comportamiento que antes, en la clase Base Duck sin baterías. Esto podría sorprender al consumidor de su clase Duck y romper la funcionalidad creada en torno al comportamiento esperado de la clase Base Duck.

Aquí hay un buen enlace: http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/


No, dice que deberías poder utilizar la clase derivada de la misma manera que su base. Hay muchas formas en que puede anular un método sin romper esto. Un ejemplo simple, GetHashCode () en C # está en la base para TODAS las clases, y aún TODOS se pueden usar como "objeto" para calcular el código hash. Un ejemplo clásico de romper la regla, por lo que recuerdo, es sacar Cuadrado de Rectángulo, ya que Cuadrado no puede tener tanto Ancho como Alto, porque establecer uno cambiaría otro y, por lo tanto, ya no cumple con las reglas de Rectángulo. Sin embargo, puede tener la Forma base con .GetSize () ya que TODAS las formas pueden hacer esto, y por lo tanto cualquier forma derivada puede ser sustituida y utilizada como Forma.


una breve explicación para (haga clic en este enlace ->) El principio de sustitución de Liskov es que si tiene una BASE de clase base y subclases SUB1 y SUB2, el resto de su código siempre debe referirse a BASE y NO a SUB1 y SUB2.
eso es todo, simple, simplemente no se refiera a las subclases