속성 중 하나가 필요하지 않은 경우 인터페이스 구현


31

꽤 직설적 인. 인터페이스를 구현하고 있지만이 클래스에 필요하지 않은 속성 중 하나는 실제로 사용해서는 안됩니다. 내 초기 아이디어는 다음과 같은 것을하는 것이 었습니다.

int IFoo.Bar
{
    get { raise new NotImplementedException(); }
}

나는 이것 자체에 아무런 문제가 없다고 생각하지만, "옳다"고 느끼지 않습니다. 다른 사람이 전에 비슷한 상황을 경험 한 적이 있습니까? 그렇다면 어떻게 접근 했습니까?


1
C #에는 인터페이스를 구현하지만 특정 방법이 구현되지 않았다는 것을 명시 적으로 문서화하는 C #에서 반 일반적으로 사용되는 클래스가 있다는 것을 모호하게 기억합니다. 나는 그것을 찾을 수 있는지 보려고 노력할 것이다.
마법사 Xy

나는 당신이 그것을 찾을 수 있다면 분명히 그것을보고 싶습니다.
Chris Pratt

23
.NET 라이브러리에서 여러 사례를 지적 할 수 있으며 모두 나쁜 끔찍한 실수로 인식 됩니다. 이것은 Liskov 대체 원칙에 대한 상징적이고 일반적인 위반입니다. LSP를 위반 하지 않는 이유 는 여기
Jimmy Hoffa

3
이 특정 인터페이스를 구현해야합니까, 아니면 슈퍼 인터페이스를 도입하여 사용할 수 있습니까?
Christopher Schultz

6
"이 클래스에 필요하지 않은 하나의 속성"-인터페이스의 일부가 필요한지 여부는 구현자가 아닌 인터페이스의 클라이언트에 달려 있습니다. 클래스가 인터페이스 멤버를 합리적으로 구현할 수 없으면 클래스가 인터페이스에 적합하지 않습니다. 이것은 인터페이스가 제대로 설계되지 않았을 수 있음을 의미 할 수 있습니다.
Sebastian Redl

답변:


51

이것은 사람들이 Liskov 대체 원칙을 위반하기로 결정한 전형적인 예입니다. 나는 그것을 강력히 권장하지 않지만 다른 해결책을 장려 할 것입니다.

  1. 아마도 작성중인 클래스가 인터페이스의 모든 멤버를 사용하지 않는 경우 인터페이스가 규정하는 기능을 제공하지 않을 수도 있습니다.
  2. 또는 해당 인터페이스에서 여러 작업을 수행 할 수 있으며 인터페이스 분리 원칙에 따라 분리 될 수 있습니다.

첫 번째 경우라면 해당 클래스에서 인터페이스를 구현하지 마십시오. 접지 구멍이 불필요하므로 실제로 접지에 연결 되지 않는 전기 소켓으로 생각하십시오 . 당신은 땅에 아무것도 꽂지 않고 큰 거래를하지 않습니다! 그러나 당신이 땅을 필요로하는 것을 사용하자마자, 당신은 장엄한 실패에 빠질 수 있습니다. 가짜 구멍을 뚫지 않는 것이 좋습니다. 따라서 클래스가 실제로 인터페이스가 의도 한 작업을 수행하지 않으면 인터페이스를 구현하지 마십시오.


다음은 Wikipedia에서 제공하는 몇 가지 간단한 정보입니다.

Liskov 대체 원칙 은 간단히 "사전 조건을 강화하지 말고 사후 조건을 약화시키지 마십시오"라고 간단히 표현할 수 있습니다.

보다 공식적으로, Liskov 대체 원칙 (LSP)은 1987 년 컨퍼런스 기조 연설에서 데이터 추상화 및 계층이라는 제목으로 Barbara Liskov에 의해 처음 도입 된 (강력한) 행동 서브 타이핑이라는 서브 타이핑 관계의 특정 정의입니다. 그것은 계층 구조에서 유형의 의미 론적 상호 운용성을 보장 하기 때문에 단지 구문 론적 관계 라기보다는 의미 론적 이다 .

동일한 계약의 서로 다른 구현 간의 의미 상호 운용성 및 대체 가능성을 위해서는 모두 동일한 행동을 수행해야합니다.


인터페이스 분리 원리 는 인터페이스가 응집력있는 세트로 분리되어야하므로 하나의 시설 만 원할 때 많은 이종 작업을 수행하는 인터페이스가 필요하지 않다는 아이디어를 말합니다 . 전기 소켓의 인터페이스를 다시 생각, 그것은 또한 자동 온도 조절 장치를 가지고 있지만, 그것은 더 열심히 전기 소켓을 설치하고 비 가열을 위해 사용 어렵게 만들 수 있습니다 만들 것입니다. 자동 온도 조절 장치가있는 전기 소켓과 같이 큰 인터페이스는 구현하기 어렵고 사용하기가 어렵습니다.

인터페이스 분리 원리 (ISP)는 클라이언트가 사용하지 않는 방법에 의존해서는 안된다고 명시하고 있습니다. [1] ISP는 매우 큰 인터페이스를 더 작고 더 구체적인 인터페이스로 분할하여 클라이언트가 관심있는 방법에 대해서만 알면됩니다.


전혀. 아마도 처음에는 그것이 나에게 옳지 않다고 느끼는 이유 일 것입니다. 때때로 당신은 당신이 바보 같은 일을하고 있다는 것을 상기시켜줍니다. 감사.
Chris Pratt

@ChrisPratt는 그것은이다 정말 형식주의가 도움이 될 수있는 이유입니다하려면 일반적인 실수 - 코드를 냄새 분류하는 것은 더 신속하게 식별 할 수 있습니다 및 리콜 솔루션은 이전에 사용.
Jimmy Hoffa

4

이것이 당신의 상황이라면 나에게 잘 보입니다.

그러나 파생 클래스가 실제로 모든 클래스를 구현하지 않으면 인터페이스 (또는 인터페이스 사용)가 손상된 것 같습니다. 해당 인터페이스를 분할 해보십시오.

면책 조항 : 제대로하려면 여러 상속이 필요하며 C #이 지원하는지 여부는 알 수 없습니다.


6
C #은 클래스의 다중 상속을 지원하지 않지만 인터페이스를 지원합니다.
mgw854

2
내 생각 엔 당신이 맞다. 인터페이스는 결국 계약이며,이 특정 클래스 가이 속성이 비활성화되어 있으면 인터페이스를 사용하는 모든 것을 중단시키는 방식으로 사용되지 않는다는 것을 알더라도 분명하지 않습니다.
Chris Pratt

@ChrisPratt : 예.
Monica와의 가벼움 경주

4

나는이 상황을 겪었다. 실제로 BCL이 그러한 사례를 가지고있는 다른 곳 에서 지적한 것처럼 ... 나는 더 나은 예를 제공하고 근거를 제시하려고 노력할 것입니다.

이미 배송 된 인터페이스가있는 경우 호환성을 위해 유지하고 ...

  • 인터페이스에는 더 이상 사용되지 않거나 분류 된 멤버가 포함되어 있습니다. 예를 들어 BlockingCollection<T>.ICollection.SyncRoot(다른 것들 중에서) ICollection.SyncRoot그 자체가 쓸모없는 것은 아니지만 던질 것 NotSupportedException입니다.

  • 인터페이스에는 선택 사항으로 문서화되고 구현시 예외가 발생할 수있는 멤버가 포함됩니다. 예를 들어 MSDN에서 다음과 IEnumerator.Reset같이 말합니다.

COM 상호 운용성을 위해 재설정 방법이 제공됩니다. 반드시 구현할 필요는 없습니다. 대신, 구현자는 단순히 NotSupportedException을 던질 수 있습니다.

  • 인터페이스 디자인의 실수로 인해 처음에는 인터페이스가 둘 이상이어야했습니다. 를 사용하여 컨테이너의 읽기 전용 버전을 구현하는 것은 BCL의 일반적인 패턴입니다 NotSupportedException. 나는 그것을 스스로했다, 그것은 지금 예상되는 것이다. .. 나는 당신이 그들에게 appart를 말할 수 있도록 ICollection<T>.IsReadOnly돌아 true간다. 올바른 디자인은 읽기 가능한 버전의 인터페이스를 갖는 것이 었으며 전체 인터페이스는 그 인터페이스를 상속합니다.

  • 더 나은 인터페이스를 사용할 수 없습니다. 예를 들어, 색인별로 항목에 액세스하고 항목이 포함되어 있는지 확인하고 어떤 색인에 어떤 크기가 있는지 배열로 복사 할 수있는 IList<T>클래스가 있습니다. 고정 크기이며 추가 또는 제거를 지원하지 않으므로 목록보다 배열처럼 작동합니다. 그러나 BCL 에는 없습니다IArray<T> .

  • 인터페이스는 여러 플랫폼으로 포팅되는 API에 속하며 특정 플랫폼을 구현할 때 일부가 지원되지 않습니다. 이상적으로는 API를 사용하는 이식 가능한 코드가 해당 부분을 호출할지 여부를 결정할 수 있도록 감지 할 수있는 방법이 있지만 이상적으로는 호출하는 것이 좋습니다 NotSupportedException. 이것이 원래 디자인에서 예견되지 않은 새로운 플랫폼의 포트라면 특히 그렇습니다.


또한 왜 지원되지 않습니까?

때로는 InvalidOperationException더 나은 옵션입니다. 예를 들어 클래스에 다형성을 추가하는 또 다른 방법은 내부 인터페이스를 다양하게 구현하고 코드 생성자가 클래스 생성자에 지정된 매개 변수에 따라 인스턴스화 할 방법을 선택하는 것입니다. [이것은 옵션 세트가 고정되어 있고 의존성 주입으로 써드 파티 클래스를 도입하지 못하게하려는 경우 특히 유용합니다.] 추적 및 비 추적 구현이 있기 때문에 ThreadLocal백 포트 하기 위해이 작업을 수행했습니다. 너무 많은 appart, 그리고 ThreadLocal.Values비 추적 구현에 어떤 영향을 미칩니 까?InvalidOperationException심지어 객체의 상태에 의존하지 않습니다. 이 경우 클래스를 직접 소개했으며 예외를 throw 하여이 방법을 구현해야한다는 것을 알았습니다.

때로는 기본값이 의미가 있습니다. 예를 들어 위에서 ICollection<T>.IsReadOnly언급 한 경우에 따라 경우에 따라 "true"또는 "false"를 반환하는 것이 좋습니다. 그래서 ...의 의미는 IFoo.Bar무엇입니까? 반환할만한 기본값이있을 수 있습니다.


부록 : 인터페이스를 제어하고 있고 호환성을 유지하기 위해 인터페이스를 유지할 필요가없는 경우에는 던져야 할 경우가 없어야합니다 NotSupportedException. 그러나 사용자의 상황에 맞는 인터페이스를 두 개 이상의 더 작은 인터페이스로 분할해야 할 수도 있습니다. 이로 인해 극한 상황에서 "오염"이 발생할 수 있습니다.


0

다른 사람이 전에 비슷한 상황을 경험 한 적이 있습니까?

예. .Net 라이브러리 디자이너가 해냈습니다. Dictionary 클래스는 수행중인 작업을 수행합니다. 명시 적 구현을 ​​사용하여 일부 IDictionary 메소드 를 효과적으로 숨 깁니다 . 그것은의 더 나은 여기에서 설명 하지만, 사전의 추가, 사용하는 CopyTo를 사용하거나 KeyValuePairs을 방법을 제거하기 위해, 요약, 당신은 먼저 IDictionary에 개체를 캐스팅해야합니다.

* 마이크로 소프트가 사용하는 것처럼 "숨기기"라는 단어의 엄격한 의미로 메소드를 "숨기는"것은 아닙니다 . 그러나 나는이 경우에 더 나은 용어를 모른다.


... 끔찍하다.
Monica와의 가벼움 경주

왜 공감해야합니까?
user2023861

2
아마도 당신이 그 질문에 대답하지 않았기 때문일 것입니다. 아이 쉬 일종의.
Monica와의 가벼움 경주

@LightnessRacesinOrbit, 더 명확하게하기 위해 답변을 업데이트했습니다. OP가 도움이되기를 바랍니다.
user2023861

2
그것은 나에게 질문에 대한 답변 인 것 같습니다. 그것이 좋은 아이디어인지 나쁜 아이디어인지는 질문의 범위 내에있는 것 같지 않지만 대답을 나타내는 데 여전히 유용 할 수 있습니다.
Ellesedil

-6

항상 양성 또는 기본값을 구현하고 반환 할 수 있습니다. 결국 그것은 단지 재산입니다. 0 (기본 속성 int)을 반환하거나 구현에 의미가있는 모든 값 (int.MinValue, int.MaxValue, 1, 42 등)을 반환 할 수 있습니다

//We don't officially implement this property
int IFoo.Bar
{
     { get; }
}

예외를 던지는 것은 나쁜 형태 인 것 같습니다.


2
왜? 잘못된 데이터를 더 잘 반환하는 방법은 무엇입니까?
Monica와의 가벼움 경주

1
이것은 LSP를 깨뜨립니다. 이유에 대한 설명은 Jimmy Hoffa의 답변을 참조하십시오.

5
올바른 반환 값이 없으면 잘못된 값을 반환하는 것보다 예외를 throw하는 것이 좋습니다. 무엇을 하든지 실수로이 속성을 호출하면 프로그램이 제대로 작동하지 않습니다. 그러나 예외를 던지면 그것이 오작동 하는지 분명 할 것 입니다.
Tanner Swett

나는 당신이 인터페이스를 구현할 때 어떻게 가치가 잘못 될지 모른다! 그것은 당신의 구현이며, 원하는대로 할 수 있습니다.
Jon Raynor

컴파일 타임에 올바른 값을 알 수 없으면 어떻게합니까?
Andy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.