Liskov 대체 원칙-재정의 / 가상 방법 없음?
Liskov 대체 원칙에 대한 나의 이해는 기본 클래스의 일부 속성이 참이거나 기본 클래스의 일부 구현 된 동작이 파생 클래스에 대해서도 참이어야한다는 것입니다.
이것은 메서드가 기본 클래스에서 정의 될 때 파생 클래스에서 재정의되어서는 안된다는 것을 의미한다고 생각합니다. 파생 된 클래스 대신 기본 클래스를 대체하면 다른 결과를 얻을 수 있기 때문입니다. 나는 이것이 또한 (순수하지 않은) 가상 방법을 갖는 것이 나쁜 것을 의미한다고 생각합니까?
원리를 잘못 이해 한 것 같습니다. 그렇지 않다면 왜이 원칙이 좋은 습관인지 이해할 수 없습니다. 누군가 나에게 이것을 설명 할 수 있습니까? 감사
기본 클래스의 메서드를 재정의하는 하위 클래스는 Liskov Substituion Principle에 의해 완전히 허용됩니다.
이것은 그것을 너무 단순화시킬 수 있지만, "하위 클래스는 더 이상 아무것도 필요로하지 않고 더 적게 약속해서는 안된다" 라고 기억합니다.
클라이언트가 ABC
메소드와 함께 수퍼 클래스 를 사용하는 경우 클라이언트는 문제없이의 something(int i)
모든 서브 클래스를 대체 할 수 있어야합니다 ABC
. 변수 유형의 관점에서 생각하는 대신, 아마도 전제 조건과 사후 조건의 관점에서 생각해보십시오.
우리의 경우 something()
의 방법을 ABC
기본 클래스가 위의 허용 편안한 전제 조건이 어떤 정수를, 다음의 모든 서브 클래스 ABC
필수는 또한 허용하는 임의의 정수를. 서브 클래스 GreenABC
는 something()
매개 변수가 양의 정수 여야하는 메소드에 추가 전제 조건을 추가 할 수 없습니다 . 이것은 Liskov 대체 원칙을 위반하는 것입니다 (즉, 더 많은 것을 요구함). 따라서 클라이언트가 하위 클래스를 사용하고 클라이언트에 BlueABC
음의 정수를 전달 something()
하면로 전환해야하는 경우 중단되지 않습니다 GreenABC
.
반대로, 기본 ABC
클래스 something()
메서드에 사후 조건이있는 경우 ( 예 : 0 값을 반환하지 않음을 보장하는) 모든 하위 클래스도 동일한 사후 조건을 준수하거나 Liskov 대체 원칙을 위반해야합니다 (즉, 더 적게 약속).
이게 도움이 되길 바란다.
오리처럼 헤엄 치면 돌팔이는 오리를 좋아하지만 배터리가 필요하면 Liskov 대체 원리를 위반하는 인기있는 예가 하나 있습니다.
간단히 말해, 누군가가 사용하는 기본 Duck 클래스가 있습니다. 그런 다음 Duck과 동일한 재정의 된 동작 (예 : 수영, 꽥꽥 등)으로 PlasticDuck을 도입하여 계층을 추가하지만 이러한 동작을 시뮬레이션하려면 배터리가 필요합니다. 이것은 기본적으로 배터리없이 Base Duck 클래스에서 이전에 수행 한 것과 동일한 동작을 배터리가 수행하도록하기 위해 Sub Class의 동작에 추가 전제 조건을 도입하고 있음을 의미합니다. 이것은 Duck 클래스의 소비자를 놀라게 할 수 있으며 Base Duck 클래스의 예상 동작을 중심으로 구축 된 기능을 손상시킬 수 있습니다.
다음은 좋은 링크입니다 -http : //lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/
아니요, 기본 클래스와 동일한 방식으로 파생 클래스를 사용할 수 있어야 함을 나타냅니다. 이것을 깨지 않고 메서드를 재정의 할 수있는 방법은 여러 가지가 있습니다. 간단한 예, C #의 GetHashCode ()는 모든 클래스의 기본이며 여전히 모든 클래스를 "객체"로 사용하여 해시 코드를 계산할 수 있습니다. 내가 기억하는 한 규칙을 위반하는 고전적인 예는 Square에서 Width와 Height를 모두 가질 수 없기 때문에 Square를 Rectangle에서 파생시키는 것입니다. 그러나 모든 셰이프가이 작업을 수행 할 수 있으므로 .GetSize ()를 사용하여 기본 셰이프를 계속 사용할 수 있으므로 파생 된 셰이프를 대체하여 Shape로 사용할 수 있습니다.
기본 메서드로 정의 된 동작을 변경하면 재정의하면 Liskov 대체 원칙이 깨집니다. 의미하는 것은:
- 자식 메서드에 대한 가장 약한 전제 조건은 기본 메서드보다 강하지 않아야합니다.
- 자식 메서드에 대한 사후 조건은 부모 메서드에 대한 사후 조건을 의미합니다. 사후 조건이 다음과 같이 형성되는 경우 : a) 메서드 실행으로 인한 모든 부작용 및 b) 반환 된 표현식의 유형 및 값.
이 두 가지 요구 사항에서 수퍼 메서드에서 예상되는 것에 영향을 미치지 않는 자식 메서드의 새로운 기능이 원칙을 위반하지 않는다는 것을 암시 할 수 있습니다. 이러한 조건을 통해 수퍼 클래스 인스턴스가 필요한 서브 클래스 인스턴스를 사용할 수 있습니다.
이러한 규칙을 준수하지 않으면 클래스는 LSP를 위반합니다. 클래스 : 고전적인 예는 다음과 같은 계층 구조 Point(x,y)
, 클래스 ColoredPoint(x,y,color)
확장 Point(x,y)
및 재정의 된 메서드 equals(obj)
에 ColoredPoint
그 색상에 의해 평등을 반영한다. 이제 하나의 인스턴스가있는 경우 Set<Point>
동일한 좌표를 가진 두 점이이 집합에서 동일하다고 가정 할 수 있습니다. 재정의 된 메서드의 경우는 그렇지 않으며 equals
일반적으로 인스턴스화 가능한 클래스를 확장하고 equals
LSP를 중단하지 않고 메서드에 사용되는 측면을 추가 할 수있는 방법이 없습니다.
따라서이 원칙을 위반할 때마다 코드에서 예상하는 상위 클래스에 대한 불변성이 충족되지 않을 때를 드러내는 잠재적 버그가 암시 적으로 도입됩니다. 그러나 실제로는 LSP를 위반하지 않는 명백한 디자인 솔루션이없는 경우가 많으므로 예를 들어 @ViolatesLSP
클래스 주석을 사용하여 클라이언트에게 다형성 집합이나 다른 종류의 클래스 인스턴스를 사용하는 것이 안전하지 않다고 경고 할 수 있습니다. Liskov 대체 원칙에 의존하는 케이스의.
원칙을 설명하는 방식이 말 그대로 정확하고 순수 가상 또는 추상적 인 방법 만 재정의하면 위반하지 않도록 보장 할 수 있습니다.
그러나 클라이언트의 관점에서 원칙, 즉 기본 클래스에 대한 참조를 사용하는 메서드를 보면. 이 메서드가 전달 된 인스턴스의 클래스를 말할 수없고 (확실히 찾으려고 시도하지 않으며 찾을 필요도없는 경우) 원칙을 위반하지 않는 것입니다. 따라서 기본 클래스 메서드를 재정의하는 것은 중요하지 않을 수 있습니다 (일부 데코레이터는 프로세스에서 기본 클래스 메서드를 호출하여이를 수행 할 수 있습니다).
클라이언트가 전달 된 인스턴스의 클래스를 알아 내야 할 것 같으면 기존 루틴을 수정하지 않고 유지 관리 작업의 일부로 새 클래스를 추가해야하므로 유지 관리 악몽에 빠지게됩니다. ( OCP 참조 )
'program tip' 카테고리의 다른 글
새로운 RazorEngine API를 사용한 템플릿 (0) | 2020.12.31 |
---|---|
DateTime 'Z'형식 지정자는 어디에 있습니까? (0) | 2020.12.31 |
서블릿 출력 스트림을 닫아야합니까? (0) | 2020.12.31 |
Why is an HTML element rather than an HTML entity? (0) | 2020.12.31 |
git 브랜치 : gh-pages (0) | 2020.12.31 |