Liskov 대체 원칙이 내부 검사 또는 오리 입력과 호환되지 않습니까?


11

오리 유형 언어에서 일반적으로 사용되는 것과 같이 물체를 스스로 검사 할 수있는 언어에서는 Liskov 대체 원칙을 볼 수 없다는 것을 올바르게 알고 있습니까?

예를 들어, 루비, 만약 클래스 B클래스에서 상속 A, 다음에 대한 모든 객체 xA, x.class반환에 가고 A있지만, 경우 x의 목적은 B, x.class반환하지 않을 A.

다음은 LSP에 대한 설명입니다.

하자 Q (x는) 개체에 대한 속성 증명할 수 X 타입의 T . 이어서 Q (y)는 물체에 대해 증명되어야 Y 타입이 S 여기서 S는 의 하위 유형 T .

예를 들어 루비에서는

class T; end
class S < T; end

속성 q (x) 에서 알 수 있듯이이 형식으로 LSP를 위반 한 경우 =x.class.name == 'T'


부가. 대답이 "예"인 경우 (LSP는 내부 검사와 호환되지 않음) 다른 질문은 다음과 같습니다. 일부 추가 조건 하에서 특수 유형 만있는 동적 언어를 보유 할 수있는 수정 된 "약한"LSP 형식이 있습니까? 의 속성 .


최신 정보. 참고로 웹에서 찾은 LSP의 또 다른 공식은 다음과 같습니다.

기본 클래스에 대한 포인터 또는 참조를 사용하는 함수는 몰래 파생 클래스의 객체를 사용할 수 있어야합니다.

그리고 또 다른:

S가 T의 선언 된 하위 유형 인 경우 S 유형의 객체는 T 유형의 객체로 취급되는 경우 T 유형의 객체가 동작 할 것으로 예상되어야합니다.

마지막으로 주석이 달립니다 :

LSP는 객체의 예상되는 동작에 관한 것입니다. 객체의 예상되는 동작이 무엇인지 분명한 경우에만 LSP를 따를 수 있습니다.

이것은 원래 것보다 약한 것으로 보이며 가능할 수도 있지만, 공식화 된 것으로보고 싶습니다. 특히 누가 예상되는 행동을 결정하는지 설명했습니다.

그렇다면 LSP는 프로그래밍 언어에서 한 쌍의 클래스의 속성이 아니라, 조상 클래스가 만족하는 주어진 속성 세트와 함께 한 쌍의 클래스의 속성입니까? 실제로 이것은 LSP와 관련하여 서브 클래스 (하위 클래스)를 구성하기 위해 조상 클래스의 모든 가능한 용도를 알아야한다는 것을 의미합니까? LSP에 따르면, 조상 클래스는 자손 클래스로 대체 가능해야합니까?


최신 정보. 나는 이미 대답을 받아 들였지만 질문을 설명하기 위해 Ruby의 구체적인 예를 하나 더 추가하고 싶습니다. 루비에서 각 클래스는 Class클래스가 클래스의 자손 이라는 의미에서 모듈입니다 Module. 하나:

class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module

module M; end
M.class # => Module

o = Object.new

o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)

2
거의 모든 현대 언어는 어느 정도의 내성을 제공하므로 질문은 실제로 Ruby에만 국한되지 않습니다.
Joachim Sauer

이해합니다. 루비를 예로 들었습니다. 나는 내성을 가진 다른 언어들에서 LSP의 "약한 형태"가 있을지도 모른다.
Alexey

제목에서 "Ruby"를 제거했습니다.
Alexey

2
짧은 대답은 호환 가능하다는 것입니다. 여기에 대부분 동의하는 블로그 게시물입니다 : 리스 코프 치환 원칙 오리 타이핑
K.Steff

2
@Alexey이 컨텍스트의 속성은 개체의 변형입니다. 예를 들어, 불변 개체에는 값이 변경되지 않는 속성이 있습니다. 좋은 단위 테스트를 보면 이러한 속성을 정확하게 테스트해야합니다.
K.Steff

답변:


29

실제 원리 는 다음과 같습니다 .

하자 q(x)개체에 대한 속성 증명할 수 x타입 T. 그런 다음 where 의 하위 유형 인 q(y)객체 y유형에 대해 증명할 수 있어야합니다 .SST

훌륭한 위키 백과 요약 :

컴퓨터 프로그램에서 S가 T의 하위 유형이면 T 유형의 객체는 S 유형의 객체로 대체 될 수 있으며 (즉, S 유형의 객체는 T 유형의 객체로 대체 될 수 있음) 해당 프로그램의 바람직한 특성 (정확성, 수행 된 작업 등)

그리고이 논문에서 인용 한 관련 인용문들 :

필요한 것은 하위 유형의 동작을 제한하는 강력한 요구 사항입니다 . 객체가 실제로 해당 유형의 하위 유형의 멤버 인 경우에도 객체의 추정 유형 사양을 사용하여 증명할 수있는 속성 은 유지해야합니다.

형식 사양에는 다음 정보가 포함됩니다
.-형식 이름;
-타입의 값 공간에 대한 설명;
-각 유형의 방법 :
--- 이름;
--- 서명 (신호 예외 포함);
--- 전제 조건 및 사후 조건의 동작.

그래서 질문에 :

오리 유형 언어에서 일반적으로 사용되는 것과 같이 물체를 스스로 검사 할 수있는 언어에서는 Liskov 대체 원칙을 볼 수 없다는 것을 올바르게 알고 있습니까?

아니.

A.class클래스를 반환합니다.
B.class클래스를 반환합니다.

보다 구체적인 유형에서 동일한 호출을 수행하고 호환 가능한 결과를 얻을 수 있으므로 LSP가 보유합니다. 문제는 동적 언어를 사용하면 결과가있을 것으로 예상되는 결과를 계속 호출 할 수 있다는 것입니다.

그러나 정적으로 구조적 (오리) 유형 언어를 고려해 봅시다. 이 경우 A.class제약 조건이 A있거나 또는 하위 유형 이어야하는 유형을 반환합니다 A. 이는 하위 유형의 하위 유형 이 결과가 해당 제한 조건을 충족하는 유형 인 A메소드 T.class를 제공해야 한다는 정적 보증을 제공합니다 .

이것은 LSP가 오리 타이핑을 지원하는 언어로 보유하고 있으며 루비와 같은 언어에서 LSP를 위반하면 언어 설계 비 호환성보다 정상적인 동적 오용으로 인해 더 많이 발생한다는 주장을 더 강력하게합니다.


1
"더 구체적인 유형으로 같은 전화를 걸고 호환 가능한 결과를 얻을 수 있기 때문에 LSP는 유지합니다." LSP는 결과가 올바르게 이해되면 결과가 동일한 경우 유지합니다. 주어진 제약 조건과 관련하여 "주"형식의 LSP가있을 수 있으며, 모든 속성 대신 주어진 제약 조건 만 충족해야합니다. 어쨌든, 나는 어떤 참조를 부탁드립니다.
Alexey

@Alexey는 LSP의 의미를 포함하도록 편집되었습니다. A가 필요한 곳에서 B를 사용할 수 있으면 LSP가 유지됩니다. Ruby의 .class가 어떻게 위반하는지 생각합니다.
Telastyn

3
@Alexey-프로그램에 포함되어 fail unless x.foo == 42있고 하위 유형이 0을 반환하는 경우에도 마찬가지입니다. 이것은 LSP의 고장이 아니며 프로그램의 정상적인 작동입니다. 다형성은 LSP를 위반하지 않습니다.
Telastyn

1
@Alexey-물론입니다. 그것이 속성이라고 가정하자. 이 경우 코드에서 하위 유형이 동일한 의미 동작을 가질 수 없으므로 LSP를 위반합니다. 그러나 동적 또는 오리 유형 언어에는 특히 특별하지 않습니다. 언어 디자인에서 위반을 유발하는 것은 없습니다. 당신이 쓴 코드는 않습니다. LSP는 프로그램 설계의 원리 (따라서입니다 기억 해야 프로그램의 정의)이 아니라 수학적 속성입니다.
Telastyn

6
@Alexey : x.class == A에 의존하는 것을 쓰면 언어가 아닌 LSP를 위반하는 것이 코드입니다 . 거의 모든 프로그래밍 언어에서 LSP를 위반하는 코드를 작성할 수 있습니다.
Andres F.

7

LSP의 맥락에서 "속성"은 유형 (또는 객체)에서 관찰 할 수있는 것입니다. 특히 "가능한 속성"에 대해 이야기합니다.

이러한 "재산"은 foo()반환 값이없는 방법이 존재할 수 있으며 문서에 명시된 계약을 따릅니다.

" class루비에있는 모든 객체의 속성 "에서와 같이이 용어를 "속성"과 혼동하지 마십시오 . 이러한 "속성"은 "LSP 속성"일 수 있지만 자동으로 동일하지는 않습니다!

이제 질문에 대한 답은 "속성"을 얼마나 엄격하게 정의 하느냐에 달려 있습니다. 당신이 말한다면 "클래스의 속성 A.class개체의 유형을 반환합니다"하고 B실제로 수행 하는 속성이 있습니다.

그러나 "속성"을 " .class리턴 A"으로 정의하면 해당 속성 B없는 것입니다.

그러나 두 번째 정의는 본질적으로 상수를 선언하는 원형 방법을 찾았으므로 유용하지 않습니다.


프로그램의 "속성"에 대한 하나의 정의 만 생각할 수 있습니다. 주어진 입력에 대해 주어진 값을 반환하거나, 더 일반적으로 다른 프로그램에서 블록으로 사용될 때 주어진 입력에 대한 다른 프로그램은 주어진 가치. 이 정의를 사용하면 " .class개체의 유형을 반환합니다 "라는 의미를 알 수 없습니다 . 그것이 의미한다면 x.class == x.class, 이것은 흥미로운 속성이 아닙니다.
Alexey

1
@Alexey : LSP의 맥락에서 "속성"의 의미에 대한 설명으로 내 질문을 업데이트했습니다.
Joachim Sauer

2
@Alexey : 논문을 살펴보면 특정 정의 나 "속성"을 찾을 수 없습니다. 아마도 CS라는 용어는 "일부 개체에 대해 관찰 / 증명 될 수있는"일반적인 CS 의미로 사용되기 때문일 수 있습니다. 다른 측정 "객체 필드"와는 아무런 관련없습니다 .
Joachim Sauer

4
@Alexey : 더 많은 것을 말해 줄 수 없습니다. "속성이 개체의 품질 이나 특성입니다"라는 정의를 사용합니다 . "색상"은 육안으로 볼 수있는 객체 의 속성 입니다. "밀도"는 재료 의 속성 입니다. "지정된 메소드가있는"은 클래스 / 객체 의 속성 입니다.
Joachim Sauer

4
@Alexey : 나는 당신이 목욕물을 가지고 아기를 던지고 있다고 생각합니다 : 단지 어떤 속성 때문에 LSP를 붙잡을 수 없다고해서 그것이 쓸모 없거나 "어떤 언어로도 붙 잡지 않았다"는 것은 아닙니다. 그러나 그 토론은 여기까지 갔다.
Joachim Sauer

5

내가 알기로는, LSP와 호환되지 않는 내성에 대해서는 아무것도 없습니다. 기본적으로, 객체가 다른 객체와 동일한 방법을 지원하는 한이 둘은 서로 바꿔야합니다. 코드가 기대하는 경우 즉, Address객체를, 다음은 A의 경우 중요하지 않습니다 CustomerAddress또는를 WarehouseAddress한 모두 제공하기 때문에, (예를 들어) getStreetAddress(), getCityName(), getRegion()getPostalCode(). 다른 유형의 객체를 사용하고 내부 검사를 사용하여 필요한 메소드 (예 : 객체 DestinationAddress를 가져 와서 Shipment배송 주소를으로 표시하는 클래스) 를 제공하는 일종의 데코레이터를 확실히 만들 수는 Address있지만 필수는 아니며 확실하지 않습니다. LSP가 적용되는 것을 막지 않습니다.


2
@Alexey : 동일한 방법을 지원하는 객체는 "동일"합니다. 이는 동일한 이름, 동일한 개수 및 유형의 인수, 동일한 리턴 유형 및 동일한 부작용을 의미합니다 (호출 코드에 표시되는 한). 방법은 완전히 다르게 작동 할 수 있지만 계약을 준수하는 한 괜찮습니다.
TMN

1
@ Alexey : 그러나 왜 그런 계약을해야합니까? 그 계약은 실제로 어떤 용도로 사용 됩니까? 나는 그런 계약이 있다면 나는 단순히 나타날 때마다 대체 할 수있는 x.class.name과를 'A' 효율적으로 만드는 x.class.name 쓸모 .
Joachim Sauer

1
@Alexey : 다시 : 다른 클래스를 확장하여 가득 찰 수없는 계약을 정의 할 수 있다고해서 LSP가 중단되지는 않습니다. 확장 불가능한 클래스를 만들었 음을 의미합니다. " 제공된 코드 블록이 유한 시간으로 끝나는 경우 반환"하는 방법을 정의하면 이행 할 수없는 계약도 있습니다. 프로그래밍이 쓸모 없다는 의미는 아닙니다.
Joachim Sauer

2
@Alexey x.class.name == 'A'는 오리 타이핑에서 안티 패턴 인지 확인하려고 시도합니다 . 결국 오리 타이핑은 "오리처럼 qua 거리고 걷는다면 오리입니다." 이처럼 행동한다면 A계약 및 명예 A내밀어, 그것은의 A인스턴스
K.Steff

1
@Alexey 명확한 정의가 제시되었습니다. 객체의 클래스는 그 행동이나 계약 또는 호출하려는 것이 아닙니다. "속성"을 "x.myField"와 같은 "개체 필드"와 동일시하고 있습니다. 이와 관련하여 속성은 유형 불 변형과 같은 수학적 속성과 비슷합니다. 또한 오리 타이핑을 원한다면 정확한 유형을 확인하는 안티 패턴입니다. 그렇다면 LSP와 덕 타이핑의 문제점은 무엇입니까? ;)
Andres F.

4

Barbara Liskov의 원본 논문을 살펴본 후 LSP가 거의 모든 언어로 만족 될 수 있도록 Wikipedia 정의를 완성하는 방법을 찾았습니다.

우선, "provable" 이라는 단어 가 정의에서 중요합니다. Wikipedia 기사에 설명되어 있지 않으며 "제한 사항"은 참조없이 다른 곳에 언급되어 있습니다.

논문의 첫 번째 중요한 인용문은 다음과 같습니다.

필요한 것은 하위 유형의 동작을 제한하는 강력한 요구 사항입니다 . 객체가 실제로 해당 유형의 하위 유형의 멤버 인 경우에도 객체의 추정 유형 사양 사용하여 증명할 수있는 속성 은 유지해야합니다.

그리고 두 번째는 타입 스펙 이 무엇인지 설명합니다 :

형식 사양에는 다음 정보가 포함됩니다.

  • 타입의 이름;
  • 유형의 값 공간에 대한 설명.
  • 각 유형의 메소드에 대해 :
    • 그것의 이름;
    • 서명 (시그널 예외 포함)
    • 전제 조건 및 사후 조건의 동작.

따라서 LSP는 주어진 형식 사양 과 관련하여 만 의미가 있으며 적절한 형식 사양 (예를 들어 빈 형식의 경우)은 아마도 모든 언어로 만족 될 수 있습니다.

나는 "제약"이 명시 적으로 언급 되었기 때문에 Telastyn의 대답 이 내가 찾던 것에 가장 가까운 것으로 생각합니다.


Telastyn, 당신이 당신의 대답에 이러한 따옴표를 추가 할 수 있다면, 나는 내 것보다 당신의 것을 인용하고 싶습니다.
Alexey

2
마크 업은 똑같지 않아서 약간의 표현을 바꾸었지만 인용문도 포함 시켰습니다.
Telastyn

죄송합니다. Joachim Sauer이 (가) 싫어하는 "유사한"속성에 대해 이미 언급했습니다. 일반적으로 기존 답변을 복원했습니다. 솔직히, 나는 당신이 무엇을 찾고 있는지 모르겠다 ...
Andres F.

아니오, "무엇으로부터 입증 되었습니까?" 너무 많은 지식을 허용하면 속성 x.class.name = 'A'이 모든 x수업에 제공 A될 수 있습니다. 형 규격은 정의되지 않은, 그리고 비공식적으로 몇 가지 징후가 주어진하지만 LSP와의 정확한 관계는 어느 아니었다. 나는 Liskov의 논문에서 내가 찾고있는 것을 이미 발견했으며 위의 질문에 대답했습니다.
Alexey

나는 당신이 색전 한 단어가 핵심이라고 생각합니다. 어떤을 위해하는 슈퍼 문서의 경우 x해당 유형의가, x.woozle얻을 것입니다 undefined, 다음 더있는 입력 x.woozle양보하지 않는 undefined적절한 하위 유형이 될 것입니다. 상위 유형은 약 문서 아무것도하지 않는 경우 x.woozle사용한다는 사실, x.woozle상위 유형에하는 산출하기 위해 일어날 undefined하위 유형에 할 수있는 무엇에 대해 아무것도 의미하지 않을 것이다을.
supercat

3

LSP에 관한 Wikipedia의 기사 를 인용하자면 , "대체 가능성은 객체 지향 프로그래밍의 원칙입니다." 이것은 프로그램 설계의 원칙이자 일부입니다. 당신이에 따라 코드를 작성하는 경우 x.class == A, 다음은 코드 LSP를 위반된다. 이러한 종류의 깨진 코드는 Java에서도 가능하며 오리 입력이 필요하지 않습니다.

오리 타이핑의 어떤 것도 LSP를 파괴하지 않습니다. 귀하의 예와 같이 오용하는 경우에만.

추가 생각 : 어쨌든 객체 클래스를 명시 적으로 검사하지 않으면 오리 타이핑의 목적을 무효화하지 않습니까?


Andres, LSP에 대한 정의를 줄 수 있습니까?
Alexey

1
@Alexey LSP의 정확한 정의는 Wikipedia에 하위 유형으로 표시됩니다. 비공식적 인 정의는 Liskov's notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).입니다. 객체의 정확한 클래스는 "프로그램의 바람직한 속성"중 하나가 아닙니다. 그렇지 않으면 오리 타이핑뿐만 아니라 일반적으로 서브 타이핑과 반대로 실행되며 Java의 맛이 포함됩니다.
Andres F.

2
@Alexey 또한 귀하의 예가 x.class == ALSP 오리 입력을 모두 위반 한다는 점에 유의하십시오 . 실제 유형을 확인하려는 경우 오리 입력을 사용하는 것은 의미가 없습니다.
Andres F.

안드레스,이 정의는 내가 이해하기에 충분히 정확하지 않습니다. 어떤 프로그램이 있습니까? 바람직한 속성은 무엇입니까? 클래스가 라이브러리에 있으면 다른 응용 프로그램에서 다른 속성을 선호 할 수 있습니다. (다음 중 하나를 내가 LSP가 주어진 프로그래밍 언어에서 한 쌍의 클래스의 속성이라고 생각했기 때문에 A는 코드의 라인이 LSP를 침해 할 수 표시되지 않습니다 A, B)를 만족하는 LSP 여부. LSP가 다른 곳에서 사용 된 코드에 의존하는 경우 어떤 코드가 허용되는지 설명되지 않습니다. 내가 여기서 뭔가를 찾을 수 있도록 노력하겠습니다 : cse.ohio-state.edu/~neelam/courses/788/lwb.pdf
알렉세이

2
@Alexey LSP는 특정 디자인을 유지합니다. 디자인에서 찾아야 할 것입니다. 일반적으로 언어의 속성이 아닙니다. 실제 정의보다 정확하지 않습니다 Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T. x.class여기서 흥미로운 특성 중 하나가 아닌 것이 분명합니다 . 그렇지 않으면 Java의 다형성 도 작동하지 않습니다. "x.class 문제"에 덕 타이핑 고유의 것은 없습니다 . 지금까지 동의하십니까?
Andres F.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.