Esta imagen está tomada de la aplicación de diseño y patrones basados en dominio: con ejemplos en C # y .NET
Este es el diagrama de clases para el Patrón de estado donde un SalesOrder
puede tener diferentes estados durante su vida útil. Solo se permiten ciertas transiciones entre los diferentes estados.
Ahora la OrderState
clase es una abstract
clase y todos sus métodos se heredan a sus subclases. Si consideramos la subclase Cancelled
que es un estado final que no permite ninguna transición a ningún otro estado, tendremos que utilizar override
todos sus métodos en esta clase para lanzar excepciones.
Ahora, ¿eso no viola el principio de sustitución de Liskov ya que una subclase no debería alterar el comportamiento de los padres? ¿Cambiar la clase abstracta en una interfaz soluciona esto?
¿Cómo se puede arreglar esto?
cancel()
cambian el estado a cancelado.Respuestas:
Esta implementación particular, sí. Si hace que los estados sean clases concretas en lugar de implementadores abstractos, se saldrá de esto.
Sin embargo, el patrón de estado al que se refiere, que es efectivamente un diseño de máquina de estado, es en general algo con lo que no estoy de acuerdo por la forma en que lo he visto crecer. Creo que hay motivos suficientes para denunciar esto como una violación del principio de responsabilidad única porque estos patrones de gestión del estado terminan siendo depósitos centrales para el conocimiento del estado actual de muchas otras partes de un sistema. Esta parte de la gestión del estado que se está centralizando requerirá a menudo reglas comerciales relevantes para muchas partes diferentes del sistema para coordinarlas razonablemente.
Imagine si todas las partes del sistema que se preocupan por el estado estuvieran en diferentes servicios, diferentes procesos en diferentes máquinas, un administrador de estado central que detalla el estado de cada uno de esos lugares efectivamente cuellos de botella en todo este sistema distribuido y creo que ese cuello de botella es una señal de una violación de SRP, así como un mal diseño en general.
Por el contrario, sugeriría que uno haga los objetos más inteligentes como en el objeto Modelo en el patrón MVC donde el modelo sabe cómo manejarse solo, no necesita un orquestador externo para administrar su funcionamiento interno o su razón.
Incluso poniendo un patrón de estado como este dentro de un objeto para que solo se administre a sí mismo, parece que haría que ese objeto sea demasiado grande. Diría que los flujos de trabajo deberían realizarse a través de la composición de varios objetos auto-responsables, en lugar de con un solo estado orquestado que maneja el flujo de otros objetos, o el flujo de inteligencia dentro de sí mismo.
Pero en ese momento es más arte que ingeniería, por lo que definitivamente es subjetivo su enfoque a algunas de esas cosas, que dice que los principios son una buena guía y sí, la implementación que enumera es una violación de LSP, pero podría corregirse para que no lo sea. Solo tenga mucho cuidado con el SRP cuando use cualquier patrón de esta naturaleza y es probable que esté seguro.
fuente
Esa es una mala interpretación común de LSP. Una subclase puede alterar el comportamiento del padre, siempre que permanezca fiel al tipo padre.
Hay una buena explicación larga en Wikipedia , que sugiere las cosas que infringirán LSP:
Personalmente, me resulta más fácil simplemente recordar esto: si estoy mirando un parámetro en un método que tiene el tipo A, ¿alguien que pase un subtipo B me causaría alguna sorpresa? Si lo hicieran, entonces hay una violación de LSP.
¿Lanzar una excepción es una sorpresa? Realmente no. Es algo que puede suceder en cualquier momento, ya sea que llame al método de envío en OrderState o Granted o Shipped. Así que tengo que dar cuenta de ello y no es realmente una violación de LSP.
Dicho esto , creo que hay mejores formas de manejar esta situación. Si escribiera esto en C #, usaría interfaces y verificaría la implementación de una interfaz antes de llamar al método. Por ejemplo, si el OrderState actual no implementa IShippable, no llame a un método de envío.
Pero entonces tampoco usaría el patrón de Estado para esta situación particular. El patrón de estado es mucho más apropiado para el estado de una aplicación que para el estado de un objeto de dominio como este.
En pocas palabras, este es un ejemplo mal diseñado del Patrón de Estado y no es una forma particularmente buena de manejar el estado de una orden. Pero podría decirse que no infringe LSP. Y el patrón del Estado, en sí mismo, ciertamente no lo hace.
fuente
CircleWithFixedCenterButMutableRadius
violación probable de LSP si el contrato del consumidorImmutablePoint
especifica queX.equals(Y)
, si los consumidores pueden sustituir libremente X por Y, y viceversa, y por lo tanto deben abstenerse de usar la clase de tal manera que dicha sustitución pueda causar problemas. El tipo podría ser capaz de definir legítimamenteequals
tal que todas las instancias se compararían como distintas, pero tal comportamiento probablemente limitaría su utilidad.(Esto está escrito desde el punto de vista de C #, por lo que no hay excepciones marcadas).
Según el artículo de Wikipedia sobre LSP , una de las condiciones de LSP es:
¿Cómo deberías entender eso? ¿Qué son exactamente las "excepciones lanzadas por los métodos del supertipo" cuando los métodos del supertipo son abstractos? Creo que esas son las excepciones que se documentan como las excepciones que pueden generar los métodos del supertipo.
Lo que esto significa es que si
OrderState.Ship()
se documenta como algo así como "arrojaInvalidOperationException
si estas operaciones no son compatibles con el estado actual", entonces creo que este diseño no rompe LSP. Por otro lado, si los métodos de supertipo no se documentan de esta manera, se viola el LSP.Pero esto no significa que este sea un buen diseño, no debe usar excepciones para el flujo de control normal, que parece muy cercano. Además, en una aplicación real, es probable que desee saber si se puede realizar una operación antes de intentar hacerlo, por ejemplo, para desactivar el botón "Enviar" en la interfaz de usuario.
fuente