Hintergrund: Ich entwickle ein Messaging-Framework. Dieser Rahmen ermöglicht:
- Senden von Nachrichten über einen Servicebus
- Abonnieren von Warteschlangen auf dem Nachrichtenbus
- Abonnieren von Themen auf einem Nachrichtenbus
Wir verwenden derzeit RabbitMQ, aber ich weiß, dass wir in naher Zukunft auf Microsoft Service Bus (on Premise) umsteigen werden.
Ich plane, eine Reihe von Schnittstellen und Implementierungen zu erstellen, sodass ich beim Wechsel zu ServiceBus lediglich eine neue Implementierung bereitstellen muss, ohne den Clientcode (dh Herausgeber oder Abonnenten) zu ändern.
Das Problem hierbei ist, dass RabbitMQ und ServiceBus nicht direkt übersetzbar sind. RabbitMQ basiert beispielsweise auf Börsen- und Themennamen, wohingegen sich ServiceBus ausschließlich um Namespaces und Warteschlangen dreht. Außerdem gibt es keine gemeinsamen Schnittstellen zwischen dem ServiceBus-Client und dem RabbitMQ-Client (z. B. haben beide möglicherweise eine IC-Verbindung, aber die Schnittstelle unterscheidet sich - nicht von einem gemeinsamen Namespace).
Ich kann also meines Erachtens eine Schnittstelle wie folgt erstellen:
public interface IMessageReceiver{
void AddSubscription(ISubscription subscriptionDetails)
}
Aufgrund der nicht übersetzbaren Eigenschaften der beiden Technologien haben die ServiceBus- und RabbitMQ-Implementierungen der obigen Schnittstelle unterschiedliche Anforderungen. Meine RabbitMq-Implementierung von IMessageReceiver könnte also so aussehen:
public void AddSubscription(ISubscription subscriptionDetails){
if(!subscriptionDetails is RabbitMqSubscriptionDetails){
// I have a problem!
}
}
Für mich verstößt die obige Zeile gegen die Ersetzbarkeitsregel von Liskov.
Ich habe darüber nachgedacht, dies umzudrehen, damit ein Abonnement eine IMessageConnection akzeptiert, aber das RabbitMq-Abonnement würde wiederum bestimmte Eigenschaften einer RabbitMQMessageConnection erfordern.
Meine Fragen lauten also:
- Bin ich richtig, dass dies LSP bricht?
- Sind wir uns einig, dass es in einigen Fällen unvermeidlich ist, oder fehle ich etwas?
Hoffentlich ist das klar und thematisch!
interface IMessageReceiver<T extends ISubscription>{void AddSubscription(T subscriptionDetails); }
. Eine Implementierung könnte dann so aussehen public class RabbitMqMessageReceiver implements IMessageReceiver<RabbitMqSubscriptionDetails> { public void AddSubscription(RabbitMqSubscriptionDetails subscriptionDetails){} }
(in Java).
interface TestInterface<T extends ISubscription>
würde so etwas eindeutig mitteilen, welche Typen akzeptiert werden und dass es Unterschiede zwischen den Implementierungen gibt.