現実の世界-リスコフ代替原理


14

背景:私はメッセージングフレームワークを開発しています。このフレームワークでは次が可能になります。

  • サービスバスを介したメッセージの送信
  • メッセージバス上のキューにサブスクライブする
  • メッセージバスのトピックを購読する

現在RabbitMQを使用していますが、近い将来にMicrosoft Service Bus(オンプレミス)に移行することを知っています。

インターフェイスと実装のセットを作成して、ServiceBusに移行するときに、クライアントコード(パブリッシャーまたはサブスクライバー)を修正せずに新しい実装を提供するだけでよいようにします。

ここでの問題は、RabbitMQとServiceBusが直接翻訳できないことです。たとえば、RabbitMQはExchangeとトピック名に依存していますが、ServiceBusは名前空間とキューに関するものです。また、ServiceBusクライアントとRabbitMQクライアントの間に共通のインターフェイスはありません(たとえば、両方にIConnectionがある場合がありますが、インターフェイスは異なります-共通の名前空間からではありません)。

そのため、次のようにインターフェイスを作成できます。

public interface IMessageReceiver{
  void AddSubscription(ISubscription subscriptionDetails)
}

2つのテクノロジーの翻訳不可能な特性により、上記のインターフェースのServiceBusとRabbitMQの実装には異なる要件があります。したがって、IMessageReceiverのRabbitMq実装は次のようになります。

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

私にとって、上記の行は、リスコフの代替可能性の規則を破ります。

SubscriptionがIMessageConnectionを受け入れるように、これを反転させることを検討しましたが、RabbitMq SubscriptionにはRabbitMQMessageConnectionの特定のプロパティが必要になります。

だから、私の質問は:

  • これがLSPに違反することを修正できますか?
  • 場合によっては避けられないこと、または何かが足りないことに同意しますか?

うまくいけば、これは明確で話題になっています!


インターフェイスに型パラメーターを追加するオプションはありますか?Java構文の観点からは、interface TestInterface<T extends ISubscription>どのタイプが受け入れられるか、実装間に違いがあることを明確に伝えることができます。
ハルク

@Hulk、私はそうは思いません。実装ごとにISubscriptionの異なる実装が必要になるからです。
ジンジャニンジャ

1
申し訳ありませんが、私が提案したかったのはでしたinterface IMessageReceiver<T extends ISubscription>{void AddSubscription(T subscriptionDetails); }。実装はpublic class RabbitMqMessageReceiver implements IMessageReceiver<RabbitMqSubscriptionDetails> { public void AddSubscription(RabbitMqSubscriptionDetails subscriptionDetails){} }(javaの)ようになります。
ハルク

回答:


11

はい、それはLSPを壊します。受け入れられる値の数を制限することによってサブクラスの範囲を狭めているため、前提条件を強化しているからです。親は受け入れることを指定しますISubscriptionが、子は受け入れません。

避けられないかどうかは議論の余地がある。このシナリオを回避するためにデザインを完全に変更したり、プロデューサーに何かをプッシュして関係を反転させたりできますか?そのようにして、データ構造を受け入れるインターフェースとして宣言されたサービスを置き換え、実装がそれらで何をしたいかを決定します。

他のオプションは、APIのユーザーに、などのスローされる例外でインターフェースに注釈を付けることにより、受け入れられないサブタイプの状況が発生する可能性があることを明示的に知らせることUnsupportedSubscriptionExceptionです。そうすることで、初期インターフェースのモデリング中に厳密な前提条件を定義し、エラーの原因となるアプリケーションの他の部分に影響を与えずに型が正しい場合に例外をスローしないことで、それを弱めることができます。


ありがとう。問題を動かさずに「反転」する方法は考えられません。たとえば、サブスクリプションは、メッセージを受信するために、RabbitMQのIModelとServiceBusの他の何かを知る必要があります。注釈付きの例外が唯一の前進方法だと思います。
ジンジャニンジャ

2

はい、あなたのコードはここでLSPを破ります。そのような場合、DDD設計パターンの腐敗防止レイヤーを使用します。1つの例を参照できます:http : //www.markhneedham.com/blog/2009/07/07/domain-driven-design-anti-corruption-layer/

アイデアは、明示的に分離することでサブシステムへの依存を可能な限り減らし、変更時のリファクタリングを最小限に抑えることです

それが役に立てば幸い !

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.