Передумови: я розробляю рамки для обміну повідомленнями. Ця рамка дозволить:
- відправлення повідомлень через службову шину
- підписавшись на черги на шині повідомлень
- передплатити теми на шині повідомлень
Наразі ми використовуємо RabbitMQ, але я знаю, що ми найближчим часом перейдемо до службової шини Microsoft (у приміщенні).
Я планую створити набір інтерфейсів та реалізацій, щоб, коли ми переходимо до ServiceBus, мені просто потрібно було б надати нову реалізацію, не змінюючи жодного клієнтського коду (тобто видавців чи підписників).
Проблема тут полягає в тому, що RabbitMQ та ServiceBus безпосередньо не перекладаються. Наприклад, RabbitMQ покладається на обміни та імена тем, тоді як ServiceBus - це все про простори імен та черги. Також між клієнтом ServiceBus та клієнтом RabbitMQ немає спільних інтерфейсів (наприклад, обидва можуть мати IConnection, але інтерфейс відрізняється - не від загального простору імен).
Отже, я можу створити інтерфейс наступним чином:
public interface IMessageReceiver{
void AddSubscription(ISubscription subscriptionDetails)
}
Завдяки неперекладним властивостям двох технологій реалізація ServiceBus та RabbitMQ вищевказаного інтерфейсу мають різні вимоги. Тож моя реалізація RabbitMq IMessageReceiver може виглядати так:
public void AddSubscription(ISubscription subscriptionDetails){
if(!subscriptionDetails is RabbitMqSubscriptionDetails){
// I have a problem!
}
}
Для мене рядок вище порушує правило Ліскова замінюваності.
Я розглядав можливість перегортати це, щоб підписка приймала IMessageConnection, але знову ж таки, підписка RabbitMq потребує конкретних властивостей RabbitMQMessageConnection.
Отже, мої запитання:
- Чи я правда, що це порушує LSP?
- Чи згодні ми, що в деяких випадках це неминуче, чи я щось пропускаю?
Сподіваємось, це зрозуміло і по темі!
interface IMessageReceiver<T extends ISubscription>{void AddSubscription(T subscriptionDetails); }
. Тоді реалізація може виглядати так public class RabbitMqMessageReceiver implements IMessageReceiver<RabbitMqSubscriptionDetails> { public void AddSubscription(RabbitMqSubscriptionDetails subscriptionDetails){} }
(у Java).
interface TestInterface<T extends ISubscription>
чітко повідомляє, які типи прийнято, і що існують відмінності між реалізаціями.