Mundo real - Principio de sustitución de Liskov

14

Antecedentes: estoy desarrollando un marco de mensajería. Este marco permitirá:

  • envío de mensajes a través de un bus de servicio
  • suscribirse a colas en el bus de mensajes
  • suscribirse a temas en un bus de mensajes

Actualmente estamos usando RabbitMQ, pero sé que nos mudaremos a Microsoft Service Bus (en las instalaciones) en un futuro muy cercano.

Planeo crear un conjunto de interfaces e implementaciones de modo que cuando nos cambiemos a ServiceBus, simplemente necesito proporcionar una nueva implementación sin modificar ningún código de cliente (es decir, editores o suscriptores).

El problema aquí es que RabbitMQ y ServiceBus no son traducibles directamente. Por ejemplo, RabbitMQ se basa en intercambios y nombres de temas, mientras que ServiceBus tiene que ver con espacios de nombres y colas. Además, no hay interfaces comunes entre el cliente ServiceBus y el cliente RabbitMQ (por ejemplo, ambos pueden tener una conexión IC, pero la interfaz es diferente, no de un espacio de nombres común).

En mi opinión, puedo crear una interfaz de la siguiente manera:

public interface IMessageReceiver{
  void AddSubscription(ISubscription subscriptionDetails)
}

Debido a las propiedades no traducibles de las dos tecnologías, las implementaciones de ServiceBus y RabbitMQ de la interfaz anterior tienen requisitos diferentes. Así que mi implementación RabbitMq de IMessageReceiver puede tener este aspecto:

public void AddSubscription(ISubscription subscriptionDetails){
  if(!subscriptionDetails is RabbitMqSubscriptionDetails){
    // I have a problem!
  }
}

Para mí, la línea anterior rompe la regla de sustituibilidad de Liskov.

Consideré cambiar esto, para que una Suscripción acepte una IMessageConnection, pero nuevamente la Suscripción RabbitMq requeriría las propiedades específicas de una RabbitMQMessageConnection.

Por lo tanto, mis preguntas son:

  • ¿Estoy en lo cierto al decir que esto rompe el LSP?
  • ¿Estamos de acuerdo en que, en algunos casos, es inevitable, o me estoy perdiendo algo?

Con suerte, esto es claro y sobre el tema!

    
pregunta GinjaNinja 24.11.2016 - 12:40

2 respuestas

11

Sí, rompe el LSP, porque está reduciendo el alcance de la subclase al limitar el número de valores aceptados, está fortaleciendo las condiciones previas. El padre especifica que acepta ISubscription pero el hijo no.

Si es inevitable es una cosa para discusión. ¿Podría tal vez cambiar completamente su diseño para evitar este escenario, tal vez cambiar la relación empujando cosas a sus productores? De esa manera, reemplaza los servicios declarados como interfaces que aceptan estructuras de datos y las implementaciones deciden qué quieren hacer con ellas.

Otra opción es permitir que el usuario de la API sepa explícitamente que puede ocurrir una situación de un subtipo inaceptable, anotando la interfaz con una excepción que se pueda lanzar, como UnsupportedSubscriptionException . Al hacerlo, define el derecho de condición previa estricta durante el modelado de la interfaz inicial y se le permite debilitarlo al no lanzar la excepción si el tipo es correcto sin afectar al resto de la aplicación que da cuenta del error.

    
respondido por el Andy 24.11.2016 - 16:05
2

Sí, tu código rompe LSP aquí. En tales casos, usaría la capa anticorrupción de los patrones de diseño de DDD. Puede ver un ejemplo allí: enlace

La idea es reducir lo más posible la dependencia del subsistema aislándolo explícitamente, para minimizar la refactorización al cambiarlo

Espero que ayude!

    
respondido por el Julien 25.11.2016 - 08:27

Lea otras preguntas en las etiquetas