LinkedList를 확장하는 스택. Liskov 대체 원칙 위반?


13

add_first (), add_last (), add_after (), remove_first (), remove_last () 및 remove ()와 같은 함수가있는 LinkedList 클래스가 있습니다.

이제 push (), pop (), peek () 또는 top ()과 같은 기능을 제공하는 클래스 스택이 있으며 이러한 메소드를 구현하기 위해 LinkedList 클래스 메소드를 확장합니다. 이것이 Liskov 대체 원칙을 위반합니까?

예를 들어, 링크 된 목록의 n 번째 위치에 노드를 추가하려면 add_after () 사례를 고려하십시오. 기본 클래스에서는 수행 할 수 있지만 Stack 클래스에서는 수행 할 수 없습니다. 여기에서 사후 조건이 약화되거나 add_after () 메소드를 수정하여 스택 맨 위에 추가합니까?

또한 위반이 아니라면 이것이 나쁜 디자인입니까? 그리고 LinkedList 클래스를 사용하여 Stack 기능을 어떻게 구현 하시겠습니까?


6
문제 클래스를 자체 목록의 하위 클래스로 정의하면 어떤 단점이 있습니까? 약간 다른 문제에 대해 묻지 만 대부분의 답변도 여기에 적용됩니다. List에서 상속하는 대신 List를 비공개 멤버로 사용하여 Stack을 만드는 것이 좋습니다.
amon

7
예. 스택의 내부 표현을 연결된 목록 (예 : 배열) 이외의 것으로 변경하지 못하게하므로 잘못된 디자인입니다. 스택이 일반적으로 지원하지 않는 작업을 노출하고 있습니다. 상속 대신 구성을 사용하십시오.
Lee

2
연장 LinkedList하시겠습니까? "상속보다 컴포지션 선호"- LinkedList스택 클래스 내부에 있을 수는 있지만 실제로는 확장하지 않습니까?
corsiKa

1
"스택은 일종의 연결 목록"이라는 것이 진정한 진술이 아니기 때문에 Liskov 대체 원칙을 만족시킬 수 없다고 말하고 싶습니다. "스택"은 실제로 인터페이스입니다 (Java 구성이 아니라 개념적으로 의미합니다). 스택은 연결된 목록 으로 구현할 수 있습니다 . 다른 사람들이 말했듯 LinkedList이 장면 뒤에서 무거운 리프팅 (구성)을 수행 하는 개인 데이터 멤버를 사용합니다 . 이렇게하면 코드 사용자가 List가 필요한 곳에서 스택을 실수로 사용할 수 없으며 그 반대도 마찬가지입니다.
Jasmijn

1
더 건전한 것이 반대 일 것 같습니다. 내가 이것을하고 있다면 아마도 링크 된 목록 클래스가 "스택"인터페이스를 구현할 수있을 것입니다. 그렇지 않으면 이것은 스택이 개념이므로 잘못된 방법입니다. 연결 된 목록은 다른 것들 중에서 스택으로 작동 할 수있는 구현입니다.
Vality

답변:


31

이제 push (), pop (), peek () 또는 top ()과 같은 기능을 제공하는 클래스 스택이 있으며 이러한 메소드를 구현하기 위해 LinkedList 클래스 메소드를 확장합니다. 이것이 Liskov 대체 원칙을 위반합니까?

아니요. 하위 유형에 메소드를 추가하는 것이 좋습니다.

예를 들어, 링크 된 목록의 n 번째 위치에 노드를 추가하려면 add_after () 사례를 고려하십시오. 기본 클래스에서는 수행 할 수 있지만 Stack 클래스에서는 수행 할 수 없습니다.

이것은 이다 LSP를 위반. LSP에 따르면 하위 유형의 인스턴스는 프로그램의 바람직한 속성을 변경하지 않고 수퍼 유형의 인스턴스로 대체 할 수 있어야합니다. 하위 유형이 메소드를 제거하면 해당 메소드를 호출하는 모든 코드가 작동을 멈 춥니 다 (또는 NoMethodError예외 또는 해당 행을 따라 무언가 가 발생 함). 분명히 "충돌하지 않는 것"은 바람직한 특성입니다.

여기에서 사후 조건이 약화되거나 add_after () 메소드를 수정하여 스택 맨 위에 추가합니까?

add_after()이 방법으로 방법을 수정하는 것은 기록 규칙 (가장 중요한 규칙)을 위반하는 것이므로 LSP 위반을 수정하는 데 도움이되지 않습니다.

그리고 LinkedList 클래스를 사용하여 Stack 기능을 어떻게 구현 하시겠습니까?

구성을 사용하여.

참고 : 위에서 쓴 모든 것은 하위 유형 지정 및 하위 분류를 혼란스럽게하는 언어 에만 적용됩니다! LSP는 서브 클래 싱이 아닌 서브 타이핑 에 관한 것 입니다. 두 가지를 혼동하지 않는 언어에서는 하위 유형하위 유형 으로 만들지 않는 Stack한 하위 클래스 를 만드는 것이 완벽하게 허용됩니다 .LinkedListLinkedList


9
역사 규칙은 무엇입니까? 인터넷에서 찾지 못했습니다.
Zapadlo

10
서브 타입의 오브젝트를 조작하고 슈퍼 타입의 메소드를 통해이를 관찰 할 때, 슈퍼 타입의 오브젝트로는 관찰 할 수 없었던 히스토리를 관찰하는 것이 불가능해야합니다. 이 규칙은 LSP를 비 기능 언어에 적용 할 수있는 규칙이 아닙니다. 다른 모든 것들은 이미 오래 전에 알려졌습니다. 사전 및 사후 조건 규칙은 함수 유형에 대한 공분산 및 반 분산 규칙의 재구성입니다. 기록 규칙은이 모든 것을 변경 가능한 데이터에 적용 할 수있게합니다. 이것이 없으면 LSP는 순전히 기능적인 언어에만 유용합니다.
Jörg W Mittag

2
Wikipedia 기사의 설명은 합리적으로 좋다고 생각합니다. wikipedia.org/wiki/Liskov_substitution_principle 그러나 항상 그렇듯이 원본 소스를 읽어야합니다.
Jörg W Mittag

@ JörgWMittag : 유형이 "역사"가 무엇인지에 대한 계약 보증에 포함하는 것이 합리적이라고 생각하지만 모든 수퍼 타입이 모든 일련의 관측을 생성 할 수 있어야한다고 생각하지는 않습니다. 하위 유형의 객체에서 가능할 수 있습니다.
supercat

1

Jörg W Mittag가 모든 것을 이미 다루었으므로 다음 부분에서 조금 더 자세히 설명합니다.

여기에서 사후 조건이 약화되거나 add_after () 메소드를 수정하여 스택 맨 위에 추가합니까?

기본적으로 일부 계층 구조가 LSP를 위반하는지 여부에 대한 질문이있는 경우 어떤 계약에 따라 달라집니다. 그렇다면 어떤 계약 add_after이 있습니까? "n 번째 위치 또는 맨 위에 노드 추가"와 같이 미쳤다고 들리면 사후 조건이 충족되고 LSP가 위반되지 않습니다. 그렇지 않으면 LSP 위반입니다.

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