인터페이스가 다른 인터페이스에서 상속 된 경우 '비어있는'것으로 간주됩니까?


12

빈 인터페이스는 일반적으로 내가 알 수있는 한, 특히 언어와 같은 속성이 지원되는 곳에서는 나쁜 습관으로 간주됩니다.

그러나 다른 인터페이스에서 상속 된 인터페이스는 '비어있는'것으로 간주됩니까?

interface I1 { ... }
interface I2 { ... } //unrelated to I1

interface I3
    : I1, I2
{
    // empty body
}

구현이 것을 아무것도 I3구현해야합니다 I1I2및 상속이 있음을 다른 클래스에서 객체 I3, 다음 (아래 참조) 같은 의미로 사용 될 수 그래서 바로 호출하는 것입니다 I3 비어 ? 그렇다면 이것을 아키텍처하는 더 좋은 방법은 무엇입니까?

// with I3 interface
class A : I3 { ... }
class B : I3 { ... }

class Test {
    void foo() {
        I3 something = new A();
        something = new B();
        something.SomeI1Function();
        something.SomeI2Function();
    }
}

// without I3 interface
class A : I1, I2 { ... }
class B : I1, I2 { ... }

class Test {
    void foo() {
        I1 something = new A();
        something = new B();
        something.SomeI1Function();
        something.SomeI2Function(); // we can't do this without casting first
    }
}

1
여러 인터페이스를 매개 변수 / 필드의 유형으로 지정할 수없는 것은 C # /. NET의 설계 결함입니다.
코드 InChaos

부분 을 더 잘 건축하는 방법 은 별도의 질문에 들어가야 한다고 생각합니다 . 현재 승인 된 답변은 질문 제목과 관련이 없습니다.
Kapol

@CodesInChaos 아니요, 이미 두 가지 방법이 없습니다. 하나는이 질문에 있고, 다른 방법은 메소드 인수에 대한 일반 유형 매개 변수를 지정하고 where 제한을 사용하여 두 인터페이스를 모두 지정하는 것입니다.
Andy

I1이 아닌 I1을 구현하는 클래스는 I3이 아닌 클래스를 사용할 수 없다는 것이 합리적 foo입니까? (답이 "예"이면 계속하십시오!)
user253751

@Andy 필자는 CodeInChaos의 주장 과 같은 결함에 전적으로 동의 하지 않지만 메소드의 일반 유형 매개 변수가 솔루션이라고 생각하지 않습니다. 메소드의 구현에 사용되는 유형을 무엇이든 알고 있어야 할 책임이 있습니다. 느낌이 잘못된 메서드를 호출합니다. 아마도 var : I1, I2 something = new A();지역 변수 와 같은 일부 구문은 좋을 것입니다 (Konrad의 답변에서 제기 된 좋은 점을 무시하면)
Kai

답변:


27

I3을 구현하는 것은 I1과 I2를 구현해야하며, I3를 상속하는 다른 클래스의 객체는 서로 바꿔서 사용할 수 있습니다 (아래 참조). I3을 비우는 것이 옳습니까? 그렇다면 이것을 아키텍처하는 더 좋은 방법은 무엇입니까?

"번들링" I1I2I3제공하는 좋은 (?) 바로 가기, 또는 당신이 모두 구현 될 원하는 위치에 대한 구문 설탕의 일종.

이 접근 방식의 문제점은 사용자가 명시 적으로 구현에서 다른 개발자를 중지 할 수 있다는 것입니다 I1 I2 나란히하지만,이 두 가지를 작동하지 않습니다 - 동안이 : I3의 동일합니다 : I1, I2, : I1, I2의 동일하지 않습니다 : I3. 그들은 기능적으로 동일한 경우에도, 클래스 지원은 모두 것을 I1I2지원으로 감지되지 않습니다 I3.

즉, "수동"과 "달콤한"(구문 설탕 포함) 같은 두 가지 방법으로 동일한 작업을 수행 할 수 있습니다. 또한 코드 일관성에 나쁜 영향을 줄 수 있습니다. 여기, 거기, 그리고 다른 곳에서 같은 일을 달성하는 두 가지 방법을 제공하면 이미 8 가지 가능한 조합이 있습니다. :)

void foo() {
    I1 something = new A();
    something = new B();
    something.SomeI1Function();
    something.SomeI2Function(); // we can't do this without casting first
}

알 수 없습니다. 그러나 아마도 실제로 좋은 징조일까요?

경우 I1I2다른 책임을 의미한다 (그렇지 않으면 처음부터 그들을 분리 할 필요가 없을 것), 어쩌면 foo()시도 할 잘못 something한 번에 두 개의 서로 다른 책임을 수행?

다시 말해, 그 foo()자체가 단일 책임 원칙에 위배 될 수 있으며 ,이를 해제하기 위해 캐스팅 및 런타임 유형 검사가 필요하다는 사실은 우리에게 경고하는 적기입니다.

편집하다:

foo구현 I1하고 무언가 를 취하는 것을 주장한다면 I2, 그것들을 별도의 매개 변수로 전달할 수 있습니다.

void foo(I1 i1, I2 i2) 

그리고 그들은 같은 대상이 될 수 있습니다.

foo(something, something);

무엇합니까 foo그들은 같은 인스턴스를 것 아닌지주의?

그러나 그것이 신경 쓰지 않는다면 (예 : 상태가 아니기 때문에) 이것이 추악하다고 생각되면 대신 제네릭을 사용할 수 있습니다.

void Foo<T>(T something) where T: I1, I2

이제 일반적인 제약 조건은 외부 효과를 다른 것으로 오염시키지 않으면 서 원하는 효과를 처리합니다. 이것은 컴파일 타임 검사를 기반으로 한 관용적 C #이며 전반적으로 더 적합하다고 느낍니다.

내가 볼 I3 : I2, I1에뮬레이트하기위한 노력의 일환으로 다소 노동 조합의 유형 (연합 유형은 함수형 언어에 존재하는 반면, C #이나 자바는하지 않습니다) 상자의 그것을 지원하지 않는 언어입니다.

언어에서 실제로 지원하지 않는 구문을 모방하려고 시도하는 것은 종종 성가신 일입니다. 마찬가지로 C #에서는 mixin 을 에뮬레이션하려는 경우 확장 메서드 및 인터페이스를 사용할 수 있습니다 . 가능한? 예. 좋은 생각? 잘 모르겠습니다.


4

클래스가 두 인터페이스를 모두 구현하는 데 특별한 중요성이 있다면 두 인터페이스를 모두 상속하는 세 번째 인터페이스를 만드는 데 아무런 문제가 없습니다. 그러나 나는 과도한 공학적 함정에 빠지지 않도록 조심할 것입니다. 클래스는 둘 중 하나 또는 둘 다에서 상속 할 수 있습니다. 내 프로그램 에이 세 가지 경우에 많은 클래스가 없다면, 두 인터페이스 모두를 만드는 것이 약간 중요합니다. 두 인터페이스가 서로 관련이없는 경우 (IComparableAndSerializable 인터페이스는 사용하지 마십시오).

두 인터페이스에서 상속되는 추상 클래스를 만들 수 있으며 I3 대신 해당 추상 클래스를 사용할 수 있습니다. 나는 당신이 그것을해서는 안된다고 말하는 것이 잘못이라고 생각하지만, 최소한 좋은 이유가 있어야합니다.


응? 단일 클래스에서 두 개의 인터페이스를 구현할 수 있습니다. 인터페이스를 상속받지 않고 구현합니다.
Andy

@Andy 단일 클래스가 두 개의 인터페이스를 구현할 수 없다고 어디서 말했습니까? "구현"대신에 "상속"이라는 단어의 사용과 관련하여 의미론. C ++에서 상속 완벽하게 정상적으로 인터페이스에 가장 가까운 일 이후 일 것 입니다 추상 클래스는.
Neil

상속 및 인터페이스 구현은 동일한 개념이 아닙니다. 여러 서브 클래스를 상속하는 클래스는 이상한 문제를 야기 할 수 있으며, 이는 여러 인터페이스를 구현하는 경우에는 해당되지 않습니다. C ++에 인터페이스가 부족하다고해서 차이가 사라지는 것은 아니며 C ++이 할 수있는 작업이 제한적이라는 것을 의미합니다. 상속은 "is-a"관계이며 인터페이스는 "can-do"를 지정합니다.
Andy

@Andy Weird는 상기 클래스에서 구현 된 메소드 간 충돌로 인한 문제입니다. 그러나 이는 더 이상 이름이 아니거나 실제로는 인터페이스가 아닙니다. 메소드를 선언하지만 구현하지 않는 추상 클래스는 이름, 인터페이스를 제외한 모든 의미에서 사용됩니다. 용어가 언어마다 바뀌지 만 참조와 포인터가 같은 개념이 아님을 의미하지는 않습니다. 그것은 논점이지만, 당신이 이것을 고집한다면, 우리는 동의하지 않을 것입니다.
Neil

"언어 간 용어 변경"아닙니다. OO 용어는 언어마다 일관됩니다. 내가 사용한 각 OO 언어의 다른 것을 의미하는 추상 클래스를 들어 본 적이 없습니다. 예, C ++에서 인터페이스의 의미를 가질 수 있지만 요점은 누군가가 해당 언어의 추상 클래스에 행동을 추가하고 해당 의미를 깨는 것을 막을 수는 없다는 것입니다.
Andy

2

빈 인터페이스는 일반적으로 나쁜 습관으로 간주됩니다

동의하지 않습니다. 마커 인터페이스 에 대해 읽어보십시오 .

일반적인 인터페이스는 구현 클래스가 지원해야하는 기능 (메소드 선언 형식)을 지정하지만 마커 인터페이스는이를 지원하지 않아도됩니다. 이러한 인터페이스의 존재는 구현 클래스의 특정 동작을 나타냅니다.


OP가 그들을 알고 있다고 생각하지만 실제로는 논란의 여지가 있습니다. 일부 저자는이를 권장하지만 (예 : "Effective Java"의 Josh Bloch), 일부는 Java가 아직 주석이 없었던 때부터 반 패턴이며 레거시라고 주장합니다. Java의 주석은 .NET의 속성과 거의 같습니다. 필자는 Java 세계에서 .NET보다 훨씬 인기가 있다는 인상을 받았습니다.
Konrad Morawski

1
나는 그들을 한 번만 사용하지만 약간의 해킹을 느낍니다. 머리 꼭대기의 단점은 상속 된 사실을 도울 수 없다는 것입니다. 따라서 클래스를 하나로 표시하면 모든 아이들이 원하는지 여부에 관계없이 표시됩니다. 그리고 그들은 instanceof다형성 대신에 나쁜 사용을 권장하는 것 같습니다
Konrad Morawski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.