실제 세계-Liskov 대체 원리


14

배경 : 메시징 프레임 워크를 개발 중입니다. 이 프레임 워크는 다음을 허용합니다.

  • 서비스 버스를 통한 메시지 전송
  • 메시지 버스의 대기열에 가입
  • 메시지 버스의 주제 구독

현재 RabbitMQ를 사용하고 있지만 가까운 시일 내에 Microsoft Service Bus (Premise)로 전환 할 것입니다.

ServiceBus로 이동할 때 클라이언트 코드 (예 : 게시자 또는 구독자)를 수정하지 않고 새로운 구현을 제공하기 만하면되도록 일련의 인터페이스 및 구현을 만들 계획입니다.

여기서 문제는 RabbitMQ와 ServiceBus를 직접 번역 할 수 없다는 것입니다. 예를 들어 RabbitMQ는 Exchange와 주제 이름에 의존하는 반면 ServiceBus는 네임 스페이스와 대기열에 관한 것입니다. 또한 ServiceBus 클라이언트와 RabbitMQ 클라이언트 사이에 공통 인터페이스가 없습니다 (예 : 둘 다 IConnection이있을 수 있지만 인터페이스는 공통 네임 스페이스가 아님).

그래서 요점으로 다음과 같이 인터페이스를 만들 수 있습니다.

public interface IMessageReceiver{
  void AddSubscription(ISubscription subscriptionDetails)
}

두 기술의 번역 할 수없는 특성으로 인해 위 인터페이스의 ServiceBus 및 RabbitMQ 구현에는 서로 다른 요구 사항이 있습니다. 따라서 IMessageReceiver의 RabbitMq 구현은 다음과 같습니다.

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

나에게 위의 줄은 Liskov의 대체 가능성 규칙을 어 기고 있습니다.

서브 스크립 션이 IMessageConnection을 허용하도록이 문제를 뒤집는 것을 고려했지만 RabbitMq 서브 스크립 션은 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){} }(자바에서) 처럼 보일 수 있습니다.
헐크

답변:


11

예, 허용되는 값의 수를 제한하여 하위 클래스의 범위를 좁히고 전제 조건을 강화하기 때문에 LSP가 중단됩니다. 부모는 허용 ISubscription하지만 자식은 허용 하지 않습니다.

불가피한 지 여부는 토론의 대상입니다. 이 시나리오를 피하기 위해 디자인을 완전히 바꿀 수 있습니까, 생산자에게 물건을 밀어서 관계를 뒤집을 수 있습니까? 그렇게하면 데이터 구조를 받아들이는 인터페이스로 선언 된 서비스를 교체 할 수 있으며 구현에 따라 원하는 구조가 결정됩니다.

다른 옵션은 API 사용자에게 다음과 같이 예외가 발생하여 인터페이스에 주석을 달아 허용 할 수없는 하위 유형의 상황이 발생할 수 있음을 명시 적으로 알리는 것 UnsupportedSubscriptionException입니다. 초기 인터페이스를 모델링하는 동안 엄격한 사전 조건을 정의하고 오류를 설명하는 응용 프로그램의 나머지 부분에 영향을 미치지 않고 유형이 올바른 경우 예외를 발생시키지 않으면 서 약화시킬 수 있습니다.


감사. 나는 단지 문제를 옮기지 않고 어떻게 '플립'하는 방법을 생각할 수 없다. 예를 들어 구독은 메시지를 수신하기 위해 RabbitMQ에 대한 IModel과 ServiceBus에 대한 다른 정보를 알아야합니다. 주석이 달린 예외가 유일한 방법이라고 생각합니다.
긴자 닌자

2

예, 귀하의 코드는 LSP를 중단합니다. 이 경우 DDD 디자인 패턴의 부패 방지 계층을 사용합니다. 하나의 예를 볼 수 있습니다 : http://www.markhneedham.com/blog/2009/07/07/domain-driven-design-anti-corruption-layer/

아이디어는 서브 시스템을 명시 적으로 격리하여 서브 시스템에 대한 종속성을 가능한 많이 줄이고 리팩토링을 최소화 할 때 리팩토링을 최소화하는 것입니다.

그것이 도움이되기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.