2010-12-06 3 views
2

아래 계층 구조에서 IFoos를 허용하면서 컴파일 할 때 IBars를 제외하는 방법이 있습니까?컴파일 타임 제한 : B가 아닌 A를 허용합니다. B : A

예 :

//IFoo defines functionality that Snafu needs to use, and so is a type restriction. 
public interface IFoo {...} 

//IBar defines supplemental required functionality that Snafu does not support. 
public interface IBar:IFoo {...} 

//Can I generate a compiler error if an IBar is used as the generic type? 
public class Snafu<T> where T:IFoo 
{ 
    public void DoSomethingWith(T myFoo) 
    { 
     //Best thing I can think of with the current hierarchy 
     if(myFoo is IBar) throw new ArgumentException("IBars are not supported"); 
    } 
} 

내가 그 작동 생각할 수있는 유일한 것은 세 번째 "플래그"인터페이스를 정의하고, IBars 아닌 모든 IFoos에 적용하는 것입니다. IFoo 및 IBar를 정의한 경우 :

public interface IAmNotAnIBar:IFoo {} 

public class Snafu<T> where T:IAmNotAnIBar {...} 

그러나이 경우 해킹과 같은 냄새가납니다. 새로운 인터페이스는 IFoo가 아닌 새로운 것을 정의하지 않으며, 개발자는 IFoo (IIRC를 사용할 수 없다는 것을 알고 있어야합니다. IFoo를 숨기고 다른 인터페이스를 공용으로 노출시킴으로써 IFoo를 숨길 수는 없습니다.) IFoo를 구현하여 좋거나 나쁜 믿음으로 컴파일 타임 검사를 우회하십시오. 더 우아한 것이 있습니까?

편집 : 지금까지 모든 응답에 감사드립니다. 아마 더 구체적인 예가 제가 처음에 왜 질문을했는지 설명 할 수 있습니다.

IFoo는 DB에 매핑되는 도메인 개체의 기본 기능을 정의한다고 가정 해 보겠습니다. 읽기/쓰기 ID 속성이 있어야합니다. 어쩌면 DTO를 방출하고 흡수 할 수있는 능력이 있어야합니다. 위의 예에서 Snafu 클래스를 처리 할 수있는 Repository를 사용하여 IFoos를 영구 저장소에 읽고 쓰며 DoSomething()은 일부 영구적 인 작업을 수행합니다. 강력하게 형식화 된 IFoo에서).

이제 IBar를 IFoos와는 다른 방식으로 채워지는 도메인 개체로 정의 해 보겠습니다. 이들은 IFoo 기능 외에도 일관된 상태를 유지하는 몇 가지 추가 Initialize() 메서드를 정의합니다. 그것들은 여전히 ​​도메인 객체 (IFoos)이며 Snafu가 메소드를 적절하게 호출 할 수 없기 때문에 IBar를 Snafu의 인스턴스에 전달할 수없고 정확한 결과를 기대할 수 없다는 점을 제외하면 시스템의 모든 영역에서 그대로 취급되어야합니다 (Initialize()가 Snafu가 가지고 있지 않으며 주어서는 안되는 외부 종속성 인 매개 변수를 취한다고 가정 해 봅시다). 대신 모든 IBars의 저장소 인 다른 클래스를 호출해야합니다. 내 목표는 컴파일 타임에 Snafu에서 DoSomething()에 대한 모든 가능한 호출의 적절한 런타임 테스트 (유닛, 통합, 기능, 수동 등)에 의존하는 대신 개발자가 뭔가 잘못하고 있음을 알리는 것이 었습니다. 그것이 IBar를 결코 통과하지 못하도록합니다.

+0

'IFoo'는 * 유일한 * 허용 가능한 타입인가? 아니면' IBar가 허용되는 경우? –

+2

IBar가 IFoo를 확장한다면 IFoo가하는 모든 일을 할 수있을뿐만 아니라 추가 작업도 할 수 있습니다 .IFoo가 예상되는 곳에서 IBar를 사용할 수없는 경우 IBar는 실제로 IFoo를 확장해서는 안됩니다. [Liskov 대체 원칙] (http : //en.wikipedia.org/wiki/Liskov_substitution_principle) – dtb

+0

@dtb : 첫 번째와 두 번째 부분에 동의하지만 첫 번째 부분은 반드시 두 번째 부분을 의미하지는 않습니다. ar은 IFoo가하는 모든 작업을 수행하지만 특정 상황에서 사용해야하는 추가 기능을 제공합니다.이러한 이유로 IBar는이 상황에서 IFoo로 사용할 수 없습니다 (예, LSP에 위배됩니다). – KeithS

답변

3

IFoo의 허용하면서 IBar의 제외하려는 경우는 (-> 자식 관계가 진정으로 부모 아니에요) IBar 정말 IFoo에서 상속 안된다고 하더군요 그.

두 인터페이스가 같은 이름의 구성원을 공유하기 때문에 다른 인터페이스를 상속해야한다는 것을 의미하지는 않습니다. 이와 같은 상속을 사용하면 부모로부터 상속받은 멤버에게 새로운 의미를 부여하여 Liskov 대체 원칙을 위반하게됩니다. 귀하의 경우 IBar도 IFoo가 아니라는 것은 명백합니다. 그렇지 않으면 사용을 제한 할 필요가 없습니다.

제 생각에 인터페이스가 리팩토링되어 좀 더 의미가 있지만 자세한 내용이 없으면 말하기가 어렵습니다.

0

이것은 이상한 디자인입니다. 왜 당신이 이것을 허락하고 싶지는 않을지 모르지만, 당신이 원하기 때문에.

IFoo가 IBar을 구현하지 않도록 구현 한 다음 구현 클래스가 두 가지를 구현하도록 할 수 있습니다.IBar에 IBar과 동일한 메소드가있는 것이 중요하다면,이를 IFoo에 복사하면 구현 클래스가 만족되어야합니다.

0

파생 클래스를 허용하지 않으려는 경우에는 의미가 없습니다. 그러면 기본적으로 IFoo가 허용됩니다.이 경우 일반 클래스는 필요 없습니다.

0

where T:IFoo이 올바르지 않습니다. 인터페이스 정의를 작은 계약으로 분해 한 다음 Snafu<T> 제약 T을 실제로 지원하는 인터페이스에만 적용하십시오.

0

IBar 이 IFoo 인이기 때문에 가능하지 않습니다.

클래스에서 파생 된 누군가가 어떤 행동을 변경했는지 걱정되는 경우 봉인 된 클래스를 사용하는 것이 좋습니다.

0

IBar이 IFoo를 상속하는지 여부는 관계가 없습니다. 객체가 인터페이스를 지원해서는 안된다는 것을 컴파일 타임에 지정할 방법이 없습니다. 가장 좋은 방법은 인터페이스를 지원하지 않는 클래스의 봉인 된 버전을 사용하고 다음과 같은 인터페이스를 지원하도록하는 것입니다 (VBash 문법에 따라 꺽쇠 괄호를 사용하지 않도록하십시오) :

Interface ISelf(Of Out T As Class) 
    Function Self() As T 
End Interface 
Interface INonBar(of Out T) 
    Inherits ISelf(of Out T As Class) 
End Interface

하나의 ISelf 인터페이스를 정의한 다음 여러 인터페이스가 "자체"메서드를 정의하지 않도록 다른 인터페이스에서 상속 할 것을 권장합니다. Bar를 구현하지 않는 Foo를 받아들이려는 메소드는 INooBar (Foo의) 유형의 매개 변수를 허용합니다. Self 메소드를 사용하여 효과적으로 Foo로 다시 작성할 수 있습니다.

덧붙여 말하자면,이 접근법은 필드가 관련이없는 두 개의 인터페이스를 구현하는 객체이거나 클래스 자체가 구현하지 않는 인터페이스를 구현하는 일부 클래스의 파생 클래스임을 지정하는 방법이 없다는 제한도 있습니다. . 후자의 사양은 함수 매개 변수에서 달성 될 수 있지만 성가신 제한으로 필드 나 다른 객체에 매개 변수를 저장하여 유사한 함수로 가져올 수있는 방식으로는 저장할 수 없습니다 (반영이 없으면 가능하지 않습니다). 매개 변수가 두 유형의 제한 조건을 충족시키는 것을 요구하는 함수로 전달 될 수있는 방식으로 두 유형의 제약 조건을 충족시키는 것으로 알려진 필드를 유형 변환하는 것).

덧붙여 말하자면, 찾고있는 특정 패턴이 비정상적인 반면, 정규 상속 패턴을 따르지 않는 개체를 갖는 것이 유용한 다른 경우가 있습니다. 예를 들어, Foo 객체는 Bar를 파생시킬 수 있으며, Bar는 Boz를 유도합니다. "Foo"유형의 객체는 "Bar"유형의 객체처럼 파손되지 않고 복제 될 수 있지만 "Boz"유형의 객체는 복제 할 수 없습니다. 위의 ICloneable (Of T)와 함께 위와 같이 Iself (T)를 정의하면 CloneableFoo와 CloneableBar 밀폐 된 형식을 정의 할 수 있으며 둘 중 하나를 받아 들일 수있는 루틴을 가질 수 있습니다 (ICloneable (Of Foo) [ICloneable CloneableFoo)!] 패턴이 약간 엉망이지만 Foo를 지원하는 것보다 더 낫다 .Clone, Boz를 가진다 .Clone은 NotSupportedException을 throw한다.

관련 문제