web-dev-qa-db-ja.com

空のインターフェイスコードは匂いがしますか?

同じ種類のオブジェクト(クエリ結果)を返す関数がありますが、共通のプロパティやメソッドはありません。共通の型を得るために、戻り値の型として空のインターフェイスを使用して、両方に「実装」しました。

もちろんそれは正しく聞こえません。いつかそれらのクラスに共通するものがあり、その共通のロジックを空のインターフェイスに移動することを望み、自分を慰めることができます。しかし、私は満足しておらず、2つの異なるメソッドを使用して次に条件付きで呼び出す必要があるかどうかを考えています。それはより良いアプローチでしょうか?

また、.NET Frameworkはタグ付けのために空のインターフェイスを使用していると言われました。

私の質問は、空のインターフェイスは設計上の問題の強い兆候なのか、それとも広く使用されているのかということです。

[〜#〜] edit [〜#〜]:興味のある人にとっては、関数型言語の差別化された共用体が、私は達成しようとしていました。 C#はまだそのコンセプトに優しいとは思えません。

[〜#〜] edit [〜#〜]:この問題について 長文 を書き、問題を説明し、ソリューションの詳細。

76
Sedat Kapanoglu

そのユースケースのデザインパターン(多くの人が「マーカーインターフェイス」に言及している)が存在するように見えますが、そのようなプラクティスの使用はコードの臭い(少なくともほとんどの場合)を示していると思います。

@ V4Vendettaが投稿したように、これを対象とする静的分析ルールがあります: http://msdn.Microsoft.com/en-us/library/ms182128(v = VS.100).aspx

型に実装が期待される空のインターフェイスがデザインに含まれている場合、おそらくインターフェイスをマーカーまたは型のグループを識別する方法として使用しています。 この識別が実行時に発生する場合、これを達成する正しい方法は、カスタム属性を使用することです。属性の有無を使用する、またはターゲットタイプを識別するための属性のプロパティ。 コンパイル時に識別を行う必要がある場合は、空のインターフェイスを使用してもかまいません。

これは、引用されたMSDNの推奨事項です。

インターフェイスを削除するか、メンバーを追加します。タイプのセットにラベルを付けるために空のインターフェースが使用されている場合は、インターフェースをカスタム属性に置き換えます。

これは、すでに投稿されているウィキペディアのリンクの批評セクションも反映しています。

マーカーインターフェイスの主な問題は、インターフェイスがクラスを実装するためのコントラクトを定義し、そのコントラクトがすべてのサブクラスに継承されることです。これは、マーカーを「実装解除」できないことを意味します。与えられた例では、シリアライズしたくないサブクラスを作成する場合(おそらく一時的な状態に依存するため)、明示的にNotSerializableExceptionをスローする必要があります(ObjectOutputStreamドキュメントごと)。

48
UrbanEsc

あなたの関数は「特定のケースに基づいて完全に異なるオブジェクトを返す」と述べていますが、それらはどれだけ違いますか? 1つはストリームライター、もう1つはUIクラス、もう1つはデータオブジェクトでしょうか?いいえ...私はそれを疑います!

オブジェクトには一般的なメソッドやプロパティがない場合がありますが、おそらくそれらの役割や使用法は似ています。その場合、 マーカーインターフェイス は完全に適切なようです。

9
ColinE

マーカーインターフェイス として使用しない場合、はい、これはコードの匂いです。

インターフェイスは、実装者が遵守するコントラクトを定義します-リフレクションを使用しない空のインターフェイスがある場合(マーカーインターフェイスの場合と同様)、Objectを(既存のものとして使用することもできます) )基本タイプ。

8
Oded

あなたは自分の質問に答えました...「特定のケースに基づいてまったく異なるオブジェクトを返す関数があります。」...まったく異なるオブジェクトを返す同じ関数が必要なのはなぜですか?これが役立つ理由はわかりませんが、良いものがあるかもしれません。その場合は共有してください。

編集:説明を考慮して、実際にマーカーインターフェイスを使用する必要があります。 「完全に異なる」は「同種」とはまったく異なります。それらが完全に異なっていた場合(共有メンバーがないだけでなく)、それはコードのにおいになります。

6
Luchian Grigore

多くの人がすでに述べているように、空のインターフェイスは「マーカーインターフェイス」として有効に使用されます。

おそらく、私が考えることができる最良の使用法は、オブジェクトを、対応するリポジトリによって処理されるドメインの特定のサブセットに属するものとして示すことです。データを取得するさまざまなデータベースがあり、それぞれにリポジトリ実装があるとします。特定のリポジトリは1つのサブセットのみを処理でき、他のサブセットからのオブジェクトのインスタンスを与えられるべきではありません。ドメインモデルは次のようになります。

//Every object in the domain has an identity-sourced Id field
public interface IDomainObject
{
   long Id{get;}
}

//No additional useful information other than this is an object from the user security DB
public interface ISecurityDomainObject:IDomainObject {}

//No additional useful information other than this is an object from the Northwind DB
public interface INorthwindDomainObject:IDomainObject {}


//No additional useful information other than this is an object from the Southwind DB
public interface ISouthwindDomainObject:IDomainObject {}

その後、リポジトリをISecurityDomainObject、INorthwindDomainObject、およびISouthwindDomainObjectに対して汎用にすることができ、コードがセキュリティオブジェクトをNorthwind DB(またはその他の順列)に渡そうとしていないことをコンパイル時に確認できます。このような状況では、インターフェースは実装規約を提供しなくても、クラスの性質に関する貴重な情報を提供します。

4
KeithS