폴 백이있는 특수 사례가 Liskov 대체 원칙을 위반합니까?


20

FooInterface다음과 같은 서명을 가진 인터페이스 가 있다고 가정 해 봅시다 .

interface FooInterface {
    public function doSomething(SomethingInterface something);
}

그리고 ConcreteFoo그 인터페이스를 구현 하는 구체적인 클래스 :

class ConcreteFoo implements FooInterface {

    public function doSomething(SomethingInterface something) {
    }

}

ConcreteFoo::doSomething()특별한 유형의 SomethingInterface객체 가 전달되면 고유 한 작업을 수행 하고 싶습니다 (라고합니다 SpecialSomething).

메서드의 전제 조건을 강화하거나 새로운 예외를 던지면 확실히 LSP 위반이지만 SpecialSomething일반 SomethingInterface객체에 폴백을 제공하면서 특수한 경우에 LSP 위반이 계속 발생 합니까? 다음과 같은 것 :

class ConcreteFoo implements FooInterface {

    public function doSomething(SomethingInterface something) {
        if (something instanceof SpecialSomething) {
            // Do SpecialSomething magic
        }
        else {
            // Do generic SomethingInterface magic
        }
    }

}

답변:


19

그것은 에 대한 계약에 다른 내용에 따라 LSP의 위반이 될 doSomething방법. 그러나 LSP를 위반하지 않더라도 코드 냄새가 거의 확실합니다.

예를 들어, 계약의 일부 경우 doSomething는 전화 것입니다 something.commitUpdates()한 번에 반환하기 전에 적어도, 그리고 특별한 경우를 위해 호출 commitSpecialUpdates()그 다음, 대신 LSP의 위반. 해도 SpecialSomethingcommitSpecialUpdates()방법은 의식적으로 같은 물건을 모두 할 수 있도록 설계되었습니다 commitUpdates()단지 선제 LSP 위반 주위 해킹 것, 그리고 하나는 지속적으로 LSP를 따른다면 정확히 해커 하나의 종류는 할 필요가 없습니다 것입니다. 이와 같은 사항이 귀하의 사건에 적용되는지 여부는 명시 적이든 암시 적이든 관계없이 해당 방법에 대한 계약을 확인하여 파악해야합니다.

이것이 코드 냄새가 나는 이유는 먼저 인수 중 하나의 구체적인 유형을 검사하면 인터페이스 / 추상 유형을 정의하는 요점이 누락되고 원칙적으로 더 이상 메소드가 작동한다고 보장 할 수 없기 때문입니다 (상상 누군가 가 호출 될 SpecialSomething가정을 가진 서브 클래스를 작성하는 경우 commitUpdates()). 우선, 이러한 특별 업데이트를 기존의 내에서 작동하게하십시오SomethingInterface; 이것이 최선의 결과입니다. 정말로 그렇게 할 수 없다면 인터페이스를 업데이트해야합니다. 인터페이스를 제어하지 않으면 원하는 작업을 수행하는 고유 한 인터페이스 작성을 고려해야합니다. 모든 인터페이스를 사용할 수없는 인터페이스를 만들 수 없다면 인터페이스를 완전히 긁어 내고 다른 콘크리트 유형을 사용하는 여러 가지 방법이 있거나 더 큰 리 팩터가 필요합니다. 우리는 당신이 이것들 중 어떤 것이 적합한 지 말하기 위해 언급 한 마법에 대해 더 많이 알아야 할 것입니다.


감사! 도움이됩니다. 가설 적으로,이 doSomething()메소드 의 목적은 타입 변환이다 SpecialSomething: 만약 그것이 수신되면 SpecialSomething수정되지 않은 객체를 반환하는 반면, 일반 SomethingInterface객체를 수신 하면 알고리즘을 실행하여 객체로 변환한다 SpecialSomething. 전제 조건과 사후 조건이 동일하게 유지되므로 계약을 위반 한 것으로 생각하지 않습니다.
Evan

1
@Evan Oh 와우 ... 흥미로운 사건입니다. 실제로 완전히 문제가되지 않을 수 있습니다. 내가 생각할 수있는 유일한 것은 새로운 객체를 생성하는 대신 기존 객체를 반환하는 경우 누군가 가 새로운 객체를 반환하는이 방법에 달려 있을 수 있지만 그 종류의 사람들이 사람들을 깨뜨릴 수 있는지 여부는 언어. 누군가 전화 y = doSomething(x)를 한 다음 3 x.setFoo(3)y.getFoo()반환 할 수 있습니까?
Ixrec

언어에 따라 문제가되지만 SpecialSomething대신 객체 의 복사본을 반환하면 쉽게 해결할 수 있습니다 . 순결을 위해, 전달 된 객체가있을 때 특별한 경우 최적화를 포기 SpecialSomething하고 더 큰 변환 알고리즘을 통해 실행하는 것을 볼 수도 있습니다 SomethingInterface. 왜냐하면 객체 이기도하기 때문에 여전히 작동해야하기 때문 입니다.
Evan

1

LSP를 위반하지 않습니다. 그러나 여전히 "유형 확인 안 함"규칙을 위반하는 것입니다. 스페셜이 자연스럽게 발생하도록 코드를 디자인하는 것이 좋습니다. 어쩌면 SomethingInterface이 작업을 수행, 또는 어쩌면 당신은 추상적 인 공장 어딘가를 주입해야 할 수있는 다른 회원을 필요로한다.

그러나 이것은 어렵고 빠른 규칙이 아니므로 그 절충의 가치가 있는지 결정해야합니다. 현재 코드 냄새가 나고 향후 개선에 방해가 될 수 있습니다. 이를 제거하는 것은 상당히 복잡한 아키텍처를 의미 할 수 있습니다. 더 많은 정보가 없으면 어느 것이 더 좋은지 알 수 없습니다.


1

아니요, 주어진 인수가 인터페이스 A를 제공 할뿐만 아니라 A2도 LSP를 위반하지 않는다는 사실을 이용하십시오.

특수 경로에 더 강한 전제 조건 (취하기로 결정한 테스트 제외)이나 더 약한 사후 조건이 없는지 확인하십시오.

C ++ 템플릿은 종종 InputIterators 를 요구 하지만 RandomAccessIterators로 호출하면 추가 보증을 제공 함으로써 더 나은 성능을 제공하기 위해 그렇게합니다 .

런타임에 대신 동적 캐스팅을 사용하여 결정해야하는 경우 잠재적 인 이익을 모두 또는 더 많이 소비 할 경로를 결정해야합니다.

특별한 경우를 활용하면 코드를 복제해야 할 수도있는 DRY (반복하지 말 것)와 KISS (Keep it Simple)가 더 복잡하기 때문에 종종 발생합니다.


0

"유형 확인 안 함"과 "인터페이스 분리"원칙 사이에는 상충 관계가 있습니다. 많은 클래스가 작업을 수행 할 수는 있지만 비효율적 인 방법을 제공하고 그 중 일부는 더 나은 방법을 제공 할 수 있고 작업을 수행 할 수있는 더 넓은 범주의 항목을 수용 할 수있는 코드가 필요합니다 (아마도 비효율적이지만) 가능한 한 효율적으로 작업을 수행하려면 모든 객체가 더 효율적인 방법이 지원되는지 여부와 다른 방법으로 사용하는 경우를 포함하는 멤버를 포함하는 인터페이스를 구현해야합니다. 객체를 수신하는 코드가 확장 인터페이스를 지원하는지 여부를 확인하고 필요한 경우 캐스팅하십시오.

개인적으로, 나는 이전의 접근 방식을 선호하지만 .NET과 같은 객체 지향 프레임 워크가 인터페이스가 기본 메소드를 지정할 수 있기를 바랍니다 (더 큰 인터페이스는 작업하기가 덜 고통 스럽습니다). 공통 인터페이스에 선택적 메소드가 포함 된 경우 단일 랩퍼 클래스는 다양한 랩 조합의 기능을 가진 오브젝트를 처리 할 수 ​​있으며 원래 랩핑 된 오브젝트에 존재하는 기능 만 소비자에게 약속합니다. 많은 기능이 서로 다른 인터페이스로 분할 된 경우 랩핑 된 객체가 지원해야하는 인터페이스의 모든 조합에 대해 서로 다른 래퍼 객체가 필요합니다.


0

Liskov 대체 원칙은 상위 유형의 계약에 따라 작동하는 하위 유형에 관한 것입니다. 따라서 Ixrec이 작성한 것처럼 LSP 위반인지 여부에 대한 정보가 충분하지 않습니다.

여기에서 위반되는 것은 공개 폐쇄 원칙입니다. 새로운 요구 사항-SpecialSomething 마술-이 있고 기존 코드를 수정 해야하는 경우 OCP를 위반 하는 입니다.

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