국가 패턴이 Liskov 대체 원칙을 위반합니까? 대한 클래스 다이어그램

이 이미지는 도메인 기반 디자인 및 패턴 적용 : C # 및 .NET의 예제 포함

여기에 이미지 설명을 입력하십시오

이는 대한 클래스 다이어그램 주 패턴 A가 SalesOrder수명 시간 동안 여러 가지 상태를 가질 수 있습니다. 다른 상태 사이에는 특정 전환 만 허용됩니다.

이제 OrderState클래스는 abstract클래스이고 모든 메소드는 서브 클래스로 상속됩니다. Cancelled다른 상태로의 전이를 허용하지 않는 최종 상태 인 서브 클래스 를 고려한다면 , override이 클래스의 모든 메소드에서 예외를 발생시켜야합니다.

Sublcass가 부모의 행동을 변경해서는 안되기 때문에 Liskov의 대체 원칙을 위반하지 않습니까? 추상 클래스를 인터페이스로 변경하면이 문제가 해결됩니까?
이 문제를 어떻게 해결할 수 있습니까?



답변

이 특정 구현입니다. 추상 구현자가 아닌 상태를 구체적인 클래스로 만들면 이것에서 벗어날 수 있습니다.

그러나 당신이 언급하고있는 상태 패턴은 효과적으로 상태 머신 디자인이라는 것이 일반적으로 내가 본 방식과는 다른 의견입니다. 이러한 상태 관리 패턴이 시스템의 다른 많은 부분의 현재 상태를 알기위한 중앙 저장소가되기 때문에이를 단일 책임 원칙을 위반하는 것으로 간주 할 수있는 충분한 근거가 있다고 생각합니다. 중앙 집중화되는이 상태 관리 부분은 합리적으로 조정하기 위해 시스템의 여러 부분과 관련된 비즈니스 규칙을 요구하지 않는 경우가 더 많습니다.

상태를 관리하는 시스템의 모든 부분이 서로 다른 서비스, 서로 다른 시스템의 서로 다른 프로세스, 각 장소의 상태를 자세히 설명하는 중앙 상태 관리자가 전체 분산 시스템에 효과적으로 병목 현상을 일으켜 병목 현상이 징후라고 생각한다고 상상해보십시오. SRP 위반 및 일반적으로 잘못된 설계.

대조적으로, 모델이 자신을 처리하는 방법을 알고있는 MVC 패턴의 Model 객체에서보다 객체를보다 지능적으로 만들도록 제안합니다. 내부 작업 또는 이유를 관리하기 위해 외부 조정자가 필요하지 않습니다.

객체 내부에 이와 같은 상태 패턴을 넣어서 자체 관리하는 것만으로도 객체를 너무 크게 만드는 것처럼 느껴집니다. 워크 플로는 다른 개체의 흐름 또는 자체의 지능 흐름을 관리하는 단일 오케스트레이션 상태가 아니라 내가 말하고자하는 다양한 자체 책임 개체의 구성을 통해 수행해야합니다.

그러나 그 시점에서이 기술보다 더 예술 그리고 그것은 확실히 말했다 것들 중 일부에 대한 접근 방식을 주관적되도록 원칙은 좋은 가이드이며, 예 구현하면 목록 입니다 LSP를 위반하지만, 수 없습니다 보정 할 수있다. 이러한 성격의 패턴을 사용할 때 SRP에 매우주의하면 안전 할 것입니다.


답변

Sublcass는 부모의 행동을 변경해서는 안됩니까?

그것은 LSP에 대한 일반적인 오해입니다. 서브 클래스는 부모 유형에 충실한 한 부모의 동작을 변경할 수 있습니다.

Wikipedia에 대한 자세한 설명이 있으며 LSP를 위반할 사항을 제안합니다.

하위 유형이 충족해야하는 여러 가지 행동 조건이 있습니다. 이것들은 계약 방법론에 의한 디자인의 용어와 유사한 용어에 자세히 설명되어 있으며, 계약이 상속과 상호 작용하는 방법에 대한 몇 가지 제한을 초래합니다.

  • 하위 유형에서는 전제 조건을 강화할 수 없습니다.
  • 하위 유형에서는 사후 조건을 약화시킬 수 없습니다.
  • 상위 유형의 변형은 하위 유형으로 유지되어야합니다.
  • 히스토리 제한 사항 ( “히스토리 규칙”). 객체는 그 방법 (캡슐화)을 통해서만 수정 가능한 것으로 간주됩니다. 서브 타입은 수퍼 타입에 존재하지 않는 메소드를 도입 할 수 있기 때문에, 이러한 메소드의 도입은 수퍼 타입에 허용되지 않는 서브 타입의 상태 변경을 허용 할 수 있습니다. 히스토리 제한은이를 금지합니다. Liskov와 Wing이 소개 한 새로운 요소였습니다. MutablePoint를 ImmutablePoint의 하위 유형으로 정의하여이 제약 조건을 위반하는 것을 예로들 수 있습니다. 이것은 불변 지점의 이력에서 생성 후 상태가 항상 동일하므로 일반적으로 MutablePoint의 이력을 포함 할 수 없기 때문에 이력 제약 조건을 위반하는 것입니다. 그러나 하위 유형에 추가 된 필드는 수퍼 유형 메소드를 통해 관찰 할 수 없으므로 안전하게 수정할 수 있습니다.

개인적으로, 나는 이것을 간단히 기억하는 것이 더 쉽다는 것을 안다 : 타입 A가있는 메소드의 매개 변수를보고 있다면 하위 유형 B를 전달하는 누군가 나를 놀라게 할 것인가? 그렇다면 LSP를 위반 한 것입니다.

예외를 던지는 것은 놀라운 일입니까? 실제로는 아닙니다. OrderState에서 Ship 메소드를 호출하든 Granted 또는 Shipped에서 호출하든 관계없이 언제든지 발생할 수 있습니다. 그래서 나는 그것을 설명해야하며 실제로 LSP의 위반이 아닙니다.

, 나는이 상황을 처리하는 더 좋은 방법이 있다고 생각합니다. C #으로 이것을 작성한다면 인터페이스를 사용하고 메소드를 호출하기 전에 인터페이스의 구현을 확인합니다. 예를 들어, 현재 OrderState가 IShippable을 구현하지 않으면 Ship 메소드를 호출하지 마십시오.

그러나이 특정 상황에서는 State 패턴을 사용하지 않을 것입니다. 상태 패턴은 이와 같은 도메인 개체의 상태보다 응용 프로그램의 상태에 훨씬 더 적합합니다.

간단히 말해서, 이것은 상태 패턴에 대해 잘 고안되지 않은 예이며 주문 상태를 처리하는 특히 좋은 방법은 아닙니다. 그러나 LSP를 위반하지는 않습니다. 그리고 국가 패턴 자체는 확실히 그렇지 않습니다.


답변

(C # 관점에서 작성되었으므로 확인 된 예외는 없습니다.)

LSP대한 Wikipedia 기사에 따르면 LSP 의 조건 중 하나는 다음과 같습니다.

하위 유형 자체가 수퍼 유형의 메소드에 의해 발생 된 예외의 하위 유형 인 경우를 제외하고 하위 유형의 메소드에 의해 새로운 예외가 발생해서는 안됩니다.

그걸 어떻게 이해해야합니까? 수퍼 타입의 메소드가 추상적 일 때 정확히“슈퍼 타입의 메소드에 의해 발생 된 예외”는 무엇입니까? 나는 이것이 수퍼 타입의 메소드에 의해 발생 될 수있는 예외로 문서화 된 예외라고 생각 합니다.

이것이 의미하는 것은 OrderState.Ship()InvalidOperationException이 상태가 현재 상태에서 지원되지 않는 경우 발생합니다 “와 같이 문서화되어 있다면이 디자인이 LSP를 깨뜨리지 않는다고 생각합니다. 반면에, 수퍼 타입 ​​메소드가 이런 식으로 문서화되지 않으면 LSP가 위반됩니다.

그러나 이것이 좋은 디자인임을 의미하는 것은 아니며, 정상적인 제어 흐름에는 예외를 사용해서는 안됩니다. 또한 실제 응용 프로그램에서는 UI에서 “Ship”버튼을 비활성화하는 등 의 작업을 수행 하기 전에 작업을 수행 할 수 있는지 알고 싶을 것입니다 .