Red de conocimiento informático - Consumibles informáticos - Comprensión visual del principio de sustitución de Richter

Comprensión visual del principio de sustitución de Richter

Estudiemos la esencia de LSP. Al aprender OO, sabemos que un objeto es una combinación de un conjunto de estados y una serie de comportamientos. El estado es la propiedad interna de un objeto y el comportamiento es la propiedad externa del objeto. Lo que expresa LSP es que los objetos en un mismo sistema de herencia deben tener las mismas características de comportamiento.

Este punto muestra la diferencia esencial entre herencia OO y herencia en la vida diaria. Por poner un ejemplo: los pingüinos están clasificados como aves en el sistema de clasificación biológica. Imitamos este sistema y diseñamos tales clases y relaciones.

Hay un método fly en la clase "Bird". Los pingüinos naturalmente heredan este método, pero los pingüinos no pueden volar. Por lo tanto, anulamos el método fly en la clase Penguin y le informamos a la persona que llama del método: Penguins. no puede volar. Esto tiene mucho sentido. Sin embargo, esto viola el LSP. Los pingüinos son un subtipo de aves, ¡pero no pueden volar! Cabe señalar que el "pájaro" aquí ya no es un pájaro en biología, es una clase y una abstracción en software.

Algunas personas dirán que es normal que los pingüinos no vuelen y que el código escrito de esta manera se puede compilar normalmente, siempre que se agregue un juicio al código del cliente que usa esta clase. ¡Pero aquí está la cuestión! En primer lugar, lo más probable es que el código de cliente y el código "Penguin" no estén diseñados al mismo tiempo. En el modelo actual de desarrollo capa por capa de subcontratación de software, ni siquiera se sabe dónde se originan los dos módulos, por lo que es así. Imposible hablar de ello. Sube y modifica el código de cliente. Es probable que el programa cliente sea parte del sistema heredado y probablemente ya no se mantenga. Si el código del cliente debe modificarse debido al diseño de dicho "Penguin", ¿quién debería asumir esta responsabilidad? (Probablemente Dios, quien le dijo que hiciera que el "Pingüino" no pudiera volar. ^_^) "Modificar el código del cliente" viola directamente el OCP. Ésta es la importancia del OCP. ¡La violación de LSP hará que el diseño existente no se pueda cerrar!

El diseño revisado es el siguiente:

Pero, ¿se trata solo de LSP? El libro ofrece un ejemplo clásico, que es otro ejemplo poco convencional: un cuadrado no es un rectángulo. Los detalles de esta paradoja se pueden encontrar en línea, por lo que no entraré en detalles.

LSP no proporcionó una solución a este problema, solo planteó esa pregunta.

Como resultado, los ingenieros comenzaron a centrarse en cómo garantizar el comportamiento de los objetos. En 1988, B. Meyer propuso la teoría del Diseño por Contrato. DbC toma prestado un conjunto de métodos de métodos formales para garantizar el comportamiento y el estado de los objetos. El concepto básico es simple:

Condición previa:

Antes de cada llamada al método, el método. debe verificar la exactitud de los parámetros pasados. Solo si son correctos se puede ejecutar el método. De lo contrario, se considerará que la persona que llama ha violado el contrato y no se ejecutará. Esto se llama condición previa.

Postcondición:

Una vez verificada la condición previa, se debe ejecutar el método y se debe garantizar que el resultado de la ejecución cumpla con el contrato. ).

Invariante:

El objeto en sí tiene un conjunto de condiciones de verificación para verificar su propio estado y garantizar que la esencia del objeto no cambie.

Las anteriores son las restricciones de un solo objeto. Para satisfacer LSP, cuando existe una relación de herencia, las condiciones previas del método en la subclase deben ser iguales o más relajadas que las condiciones previas del método anulado en la superclase y las condiciones posteriores del método en la subclase deben ser; ser las mismas que las de la superclase. Las condiciones posteriores del método anulado son las mismas o más estrictas.

Algunas características en los lenguajes OO pueden ilustrar este problema:

Al heredar y. anulando el método de la superclase, la visibilidad de los métodos de la subclase debe ser igual o mayor que la visibilidad de los métodos de la superclase. Las excepciones marcadas lanzadas por los métodos de la subclase solo pueden ser subclases de las excepciones marcadas lanzadas por los métodos correspondientes. la superclase.

SuperClase de clase pública{

método A() vacío público arroja una excepción{}

}

SubClaseA de clase pública extiende SuperClase{

//Esta anulación es ilegal.

El método privado voidA() lanza IOException{}

}

La clase pública SubClassB extiende la SuperClass{

//Esta anulación está bien.

El método public voidA() lanza FileNotFoundException{}

}

A partir de Java5, las subclases The El valor de retorno del método también puede ser una subclase del valor de retorno del método de superclase correspondiente. Esto se llama "Covariante"

SuperClase de clase pública {

Número público caculate(){

return null;

}

}

La clase pública SubClass extiende la SuperClass{

//solo se compila en Java 5 o posterior.

público Entero caculate( ){

return null;

}

}

Se puede ver que las características anteriores siguen muy bien a LSP. ¿Pero qué pasa con DbC? Desafortunadamente, los principales lenguajes orientados a objetos (ya sean lenguajes dinámicos o estáticos) aún no han agregado soporte para DbC. Pero con el surgimiento del concepto AOP, creo que DbC pronto se convertirá en una de las características importantes del lenguaje OO.

Algunas digresiones:

Hace un tiempo, los dos artículos "Sonando la sentencia de muerte de la era OO" y "Por quién doblan las campanas" atrajeron innumerables discusiones. Se mencionan muchas deficiencias de los lenguajes OO. De hecho, siguiendo LSP y OCP, ya sea un sistema de tipo estático o dinámico, siempre que sea un diseño OO, debe haber restricciones estrictas en el comportamiento de los objetos. Esta restricción no sólo se refleja en la firma del método, sino también en el comportamiento específico en sí. Este es el verdadero significado de LSP y DbC. Desde este punto de vista, no se pueden explicar las ventajas y desventajas de los lenguajes dinámicos donde "todo es un objeto" y los lenguajes de "programación por interfaz" como "C++, Java", ambos tipos de lenguajes. Es necesario mejorar. La visión del hermano Zhuang sobre DJ ha comenzado a introducir el concepto de DbC. Todavía vale la pena esperar esto. ^_^

Además, la semántica de las interfaces se fortalece continuamente mediante conceptos como OCP, LSP y DbC. Las interfaces expresan la relación de "contrato" entre los comportamientos de los objetos. En lugar de simplemente servir como azúcar sintáctico para implementar la herencia múltiple.