他のインターフェイスから継承する場合、インターフェイスは「空」と見なされますか?


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の設計上の欠陥です。
-CodesInChaos

この部分をアーキテクチャ化するより良い方法は、別の質問に入るべきだと思います。現在、受け入れられている回答は質問のタイトルとは関係ありません。
カポール

@CodesInChaosいいえ、これを行うには2つの方法があるのでそうではありません。1つはこの疑問です。もう1つの方法は、メソッド引数にジェネリック型パラメーターを指定し、where制限を使用して両方のインターフェイスを指定することです。
アンディ

I3ではなくI1とI2を実装するクラスは、使用できないはずだというのは理にかなってますfooか?(答えが「はい」の場合、先に進みます!)
user253751

@Andyそれ自体が欠陥であるというCodesInChaosの主張には完全には同意しませんが、メソッドのジェネリック型パラメーターは解決策ではないと思います-メソッドの実装で使用される型を何でも知る責任を押し付けますが間違っていると感じるメソッドを呼び出しています。おそらくvar : I1, I2 something = new A();、ローカル変数のようないくつかの構文がいいでしょう(Konradの答えで提起された良い点を無視する場合)
Kai

回答:


27

I3を実装するものはすべてI1とI2を実装する必要があり、I3を継承するさまざまなクラスのオブジェクトは交換可能に使用できます(以下を参照)。もしそうなら、これをアーキテクチャ化するより良い方法は何でしょうか?

"バンドル" I1I2into I3は、両方を実装したい場所に適した(?)ショートカット、またはある種の構文シュガーを提供します。

このアプローチの問題は、あなたが明示的に実装するから他の開発者を止めることはできないということですI1 I2並んで、それは両方の方法を動作しません-しばらくは: I3相当である: I1, I2: I1, I2と同等ではありません: I3。機能的に同一であってもI1、両方をサポートするクラスで、サポートI2として検出されませんI3

これは、同じことを達成するための2つの方法を提供していることを意味します-「手動」と「甘い」(構文シュガーを使用)。そして、それはコードの一貫性に悪影響を及ぼす可能性があります。ここで同じことを達成するために2つの異なる方法を提供し、別の場所ですでに8つの可能な組み合わせになります:)

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

OK、できません。しかし、実際には良い兆候でしょうか?

異なる責任I1I2含意する場合(そもそもそれらを分離する必要はない)、2つの異なる責任を一度に実行foo()しようとするのは間違っているかもしれませんsomething

言い換えれば、それfoo()自体が単一責任の原則に違反している可能性があり、それをキャストするためにキャストとランタイムタイプのチェックが必要であるという事実は、それについて警告する赤い旗です。

編集:

あなたはそれを確実にすることを主張した場合foo実装することを何かを取るI1I2、あなたは別のパラメータとして渡すことができます:

void foo(I1 i1, I2 i2) 

そして、それらは同じオブジェクトである可能性があります。

foo(something, something);

何がfoo、彼らは同じインスタンスしているか、いない場合は注意して!

しかし、それが気になる場合(例えば、ステートレスではないため)、またはこれが見苦しいと思われる場合は、代わりにジェネリックを使用できます:

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

現在、一般的な制約は、外部の世界を何も汚染しないで、望ましい効果を処理します。これは、コンパイル時のチェックに基づいた慣用的なC#であり、全体的に適切であると感じています。

私はI3 : I2, I1、それをサポートしていない言語でユニオン型をエミュレートする努力といくらか見ています(C#またはJavaはサポートしませんが、ユニオン型は関数型言語に存在します)。

言語で実際にサポートされていない構造をエミュレートしようとすることは、しばしば不格好です。同様に、C#では、mixinsをエミュレートする場合、拡張メソッドとインターフェイスを使用できます。可能?はい。良いアイデア?よく分かりません。


4

クラスが両方のインターフェースを実装することで特に重要な場合、両方から継承する3番目のインターフェースを作成しても何も問題はありません。ただし、過剰なエンジニアリングのtrapに陥らないように注意します。クラスは、どちらか、または両方から継承できます。これらの3つのケースでプログラムに十分なクラスがない限り、両方のインターフェイスを作成するのは少し大変です。これら2つのインターフェイスが相互に関係がない場合は確かにそうではありません(IComparableAndSerializableインターフェイスはご遠慮ください)。

両方のインターフェイスを継承する抽象クラスも作成でき、その抽象クラスをI3の代わりに使用できることを思い出してください。すべきではないと言うのは間違っていると思いますが、少なくとも正当な理由があるべきです。


え?もちろん、1つのクラスに2つのインターフェイスを実装できます。インターフェイスを継承せず、実装します。
アンディ

@Andy 1つのクラスで2つのインターフェイスを実装できないとはどこで言いましたか?「実装」の代わりに「継承」という言葉を使用することに関係するのは、セマンティクスです。C ++では、インターフェイスに最も近いもの抽象クラスであるため、継承は完全に正常に機能します。
ニール

継承とインターフェースの実装は同じ概念ではありません。複数のサブクラスを継承するクラスは、いくつかの奇妙な問題を引き起こす可能性がありますが、これは複数のインターフェースを実装する場合には当てはまりません。また、C ++のインターフェイスが欠けているからといって、違いがなくなるわけではありません。つまり、C ++でできることは限られているということです。継承は「is-a」関係ですが、インターフェイスは「can-do」を指定します。
アンディ

はい、前述のクラスに実装されたメソッド間の衝突によって引き起こされる@Andy Weirdの問題。ただし、それはもはや名前でも実務でもないインターフェースではありません。メソッドを宣言するが実装しない抽象クラスは、名前、インターフェースを除くすべての意味で使用されます。用語は言語によって異なりますが、それは参照とポインターが同じ概念ではないという意味ではありません。これは重要な点ですが、これを主張する場合は、同意しないことに同意する必要があります。
ニール

「用語は言語によって変わります」いいえ、違います。オブジェクト指向の用語は、言語間で一貫しています。私が使用した各OO言語の他の何かを意味する抽象クラスを聞いたことがありません。はい、C ++のインターフェイスのセマンティクスを使用できますが、ポイントは、誰かがその言語の抽象クラスに動作を追加し、それらのセマンティクスを破ることを妨げるものは何もないということです。
アンディ

2

空のインターフェイスは一般的に悪い習慣と見なされます

同意しません。マーカーインターフェイスについてお読みください。

典型的なインターフェースは実装クラスがサポートしなければならない機能を(メソッド宣言の形式で)指定しますが、マーカーインターフェースはそうする必要はありません。このようなインターフェイスが存在するだけで、実装クラスの特定の動作を示します。


OPはそれらを認識していると思いますが、実際には少し物議をかもしています。一部の著者はそれらを推奨していますが(たとえば、「Effective Java」のJosh Bloch)、Javaがまだアノテーションを持たない時代からのアンチパターンでありレガシーであると主張する人もいます。(Javaの注釈は、.NETの属性とほぼ同等です)。私の印象では、Javaの世界では.NETよりもはるかに人気があります。
コンラッド・モラウスキー

1
私は時々それらを使用しますが、それはちょっとハックのように感じます-私の頭の上にあるマイナス面は、あなたがそれらが継承されているという事実を助けることができないということですあなたがそれを望むかどうかに関係なくマークされます。そして、彼らinstanceofは多型の代わりに使用する悪い習慣を奨励しているようです
コンラッド・モラウスキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.