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 utilizando RabbitMQ, pero sé que nos trasladaremos a Microsoft Service Bus (en las instalaciones) en un futuro muy cercano.
Planeo crear un conjunto de interfaces e implementaciones para que cuando pasemos a ServiceBus, simplemente necesite proporcionar una nueva implementación sin modificar el código del cliente (es decir, editores o suscriptores).
El problema aquí es que RabbitMQ y ServiceBus no son directamente traducibles. Por ejemplo, RabbitMQ se basa en intercambios y nombres de temas, mientras que ServiceBus se trata de 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).
Entonces, 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. Entonces mi implementación de RabbitMq de IMessageReceiver puede verse así:
public void AddSubscription(ISubscription subscriptionDetails){
if(!subscriptionDetails is RabbitMqSubscriptionDetails){
// I have a problem!
}
}
Para mí, la línea de arriba rompe la regla de sustituibilidad de Liskov.
Pensé en cambiar esto, para que una Suscripción acepte una IMessageConnection, pero nuevamente la Suscripción RabbitMq requeriría propiedades específicas de una RabbitMQMessageConnection.
Entonces, mis preguntas son:
- ¿Estoy en lo cierto de que esto rompe LSP?
- ¿Estamos de acuerdo en que en algunos casos es inevitable o me estoy perdiendo algo?
¡Con suerte, esto está claro y sobre el tema!
interface IMessageReceiver<T extends ISubscription>{void AddSubscription(T subscriptionDetails); }
. Una implementación podría verse así public class RabbitMqMessageReceiver implements IMessageReceiver<RabbitMqSubscriptionDetails> { public void AddSubscription(RabbitMqSubscriptionDetails subscriptionDetails){} }
(en Java).
interface TestInterface<T extends ISubscription>
comunicaría claramente qué tipos son aceptados, y que hay diferencias entre las implementaciones.