2012-07-13 2 views
1

나는 위의 원칙에 대한 나의 이해를 정립하고, wikipedia 항목을 반복해서 읽음으로써 그렇게하려고한다.Liskov 대체 원리 이해

공산주의와 Contravariance의 개념을 제쳐두고 나에게 슬픔을 안겨주는 wikipedia는 또한 상위 유형의 불변성을 하위 유형과 역사 제약 또는 역사 규칙에 보존해야한다고 언급합니다. 마지막 두 개념을 바탕으로 나는이 작은 예를 내놓았다 :

class Program 
{ 
    static void Main(string[] args) 
    { 
     var fooUser = new FooUser(); 

     var fooBase = new FooBase("Serge"); 

     var fooDerived = new FooDerived("Serge"); 

     fooUser.Use(fooBase); //will print "Serge" 
     fooUser.Use(fooDerived); //will print "New User" 

     Console.ReadKey(); 
    } 
} 

public class FooUser 
{ 
    public void Use(IFoo foo) 
    { 
     foo.DoSomething(); 
     Console.WriteLine(foo.Name); 
    } 
} 

public interface IFoo 
{ 
    string Name { get; } 
    void DoSomething(); 
} 

public class FooBase : IFoo 
{ 
    public string Name { get; protected set; } 

    public FooBase(string name) 
    { 
     Name = name; 
    } 

    public virtual void DoSomething() 
    { 
    } 
} 

public class FooDerived : FooBase 
{ 
    public FooDerived(string name) : base(name) 
    { 
    } 

    public override void DoSomething() 
    { 
     Name = "New Name"; 

     base.DoSomething(); 
    } 
} 

그래서 내 질문은 : 두 위에서 언급 한 개념을 기반으로,이 예제 원칙을 위반입니까? 그렇지 않다면, 왜?

대단히 감사합니다.

답변

3

LSP를 위반하려면 클래스 인터페이스에서 일부 가정을하는 클라이언트 클래스가 필요합니다. 가정은 공식적인 방식으로 정확하게 표현되어서는 안되며 때로는 사용법의 맥락에서 오는 경우도 있습니다.

당신은 요소를 추가 할 수있는 열거 형 클래스가 있습니다. 클라이언트의 가정은 예를 들어 N 개의 요소를 추가하면 정확히 N 개의 요소를 컬렉션에서 읽을 수 있습니다. 그런 다음 추가 할 때 중복 요소를 제거하는 모음을 모음에서 파생시킵니다. N 요소가 추가 되더라도 N 요소를 읽을 수있는 경우가 있기 때문에 클라이언트의 기대가 잘못되었습니다.

나에게 LSP 위반은 몇 가지 기대를 정의하는 컨텍스트가 필요합니다. 코드에는 기대치가 없으므로 LSP가 위반되지 않습니다.

이 문맥의 필요성은 동일한 클라스가 다른 문맥에서 LSP를 위반하지 않는 한 두 클라스가 하나의 클라이언트 문맥에 대해 LSP를 위반할 수 있다는 것을 의미한다.슈퍼 유형 중 하나
1. Invarint이 하위 유형 또는
3. 포스트에 의해 강화되는 하위 유형 또는 슈퍼 유형의
2. 전제 조건으로 위반하는 경우

+0

귀하의 설명은 좋은 관점을 제공합니다. – vibhu

1

여기 LSP를 위반하지 않는 것으로 보입니다. 이론적으로 우리는 FooBase의 불변량에 대해 아무것도 모르기 때문에 의심 할 여지없이 작은 창을 떠날 것입니다. 그러나 명백한 문제가없는 결과를 합리적으로 추측합니다.

불변성이 있다고 가정하면 파생 클래스가 Name의 값이 기본 클래스가없는 개체의 수명 동안 변경되도록 허용하는 내역 원칙 문제가 남습니다. 이것은 작은 세부 사항이 아니라면 분명히 LSP를 위반 한 것 같습니다 : Nameprotected 세터입니다.

보호 된 세터는 FooBase의 저자는 기본 클래스가이 일을 발생하지 않는 경우에도 개체의 수명 기간 동안 Name의 값을 변경 파생 클래스를 기대하고 있다는 것을 의미한다. 이 값을 가져오고 설정하는 데는 다른 액세스 레벨을 가질 수없는 protected 필드 name과 대조하십시오.

+0

설정할 수 있습니다 파생 클래스 사실은'Name' 반드시 인스턴스가 * 대중에 노출 된 후 그러한 변경이 합법적으로 * 발생할 수 있다는 기대를 의미하지는 않습니다. setter는, 팩토리가 작업을 개시 할 때까지, 오브젝트의 적절한 이름을 모르는 팩토리 메소드 용으로 만 사용되는 것이 전적으로 가능합니다. – supercat

0

이 예제는 C#이 클래스 invariants를 표현하는 깨끗한 방법을 가지고 있지 않기 때문에 부분적으로 말하기에 충분하지 않습니다. 아니면, 만약 그렇다면, 나는 그것을 알지 못합니다.

FooBase가 Name이 Name에 허용되는 값의 범위를 변경하거나 표현하지 않는다고 보장하지 않았기 때문에, 당신이 invariant를 위반하지 않았다고 말할 수 있습니다. 정반대 - 이름에 대한 보호 된 설정기를 포함함으로써 FooBase는 값이 값이 파생 클래스의 내부 메커니즘에 의해 변경 될 수 있다는 예상을 만듭니다.

0

LSP 위반이 본질적으로 위반 조건은 하위 유형에 의해 약화됩니다.

위의 세 가지 조건은 인터페이스 디자인 중에 공식적인 'Design by Contract'에 의해 미리 결정되어야합니다.
예에서 FooBase는 확장 클래스에서 준수해야하는 공식 규칙을 정의하지 않습니다.
게다가 setter라는 이름의 보호 된 액세스 권한이 있습니다. 그러므로 나는 LSP가 위반되었다고 생각하지 않는다. LSP 위반이 테스트 케이스에 대한 분석하면

그러나


때때로 그것은 또한 도움이 될 수 있습니다.

이 테스트 사례는 의미가 있습니다 :
fooUser.Use (fooBase); // "Serge"를 인쇄합니다
String nameDerived = fooUser.Use (fooDerived); // (A 사양으로 necessiated으로) 분명, 다음,

같은 테스트 케이스가 존재하는 경우 (nameDerived은 "방탄복을"동일) "새 사용자"(가정이 반환 이름)

어설 인쇄됩니다 위의 예는 LSP를 위반합니다.

내 블로그 그래서 더 : http://design-principle-pattern.blogspot.in/2013/12/liskov-substitution-principle.html