マーカーインターフェイスの目的は何ですか?


回答:


77

これは、「ミッチウィート」の反応に基づく接線です。

一般に、人々がフレームワークの設計ガイドラインを引用しているのを目にするときはいつでも、次のことを述べておきます。

通常、フレームワークの設計ガイドラインはほとんど無視する必要があります。

これは、フレームワークの設計ガイドラインに問題があるためではありません。.NETフレームワークは素晴らしいクラスライブラリだと思います。その素晴らしさの多くは、フレームワークの設計ガイドラインに基づいています。

ただし、設計ガイドラインは、ほとんどのプログラマーが記述したほとんどのコードには適用されません。彼らの目的は、何百万人もの開発者が使用する大きなフレームワークの作成を可能にすることであり、ライブラリの記述をより効率的にすることではありません。

その中の多くの提案は、次のことを行うようにあなたを導くことができます:

  1. 何かを実装する最も簡単な方法ではないかもしれません
  2. 余分なコードが重複する可能性があります
  3. ランタイムオーバーヘッドが増える可能性があります

.netフレームワークは非常に大きいです。それは非常に大きいので、誰もがそのすべての側面について詳細な知識を持っていると仮定することは絶対に無理です。実際、ほとんどのプログラマは、これまでに使用したことがないフレームワークの部分に頻繁に遭遇すると想定する方がはるかに安全です。

その場合、APIデザイナーの主な目標は次のとおりです。

  1. 残りのフレームワークと一貫性を保つ
  2. API表面領域の不要な複雑さを排除

フレームワーク設計ガイドラインは、開発者にこれらの目標を達成するコードを作成するように促します。

つまり、コードの複製を意味する場合でも、継承のレイヤーを回避することや、共有ヘルパーを使用するのではなく、すべての例外をスローしてコードを「エントリポイント」にプッシュすることにより(デバッガーでスタックトレースがより意味をなすようになります)、多くのことを行います。他の同様のもの。

これらのガイドラインがマーカーインターフェイスの代わりに属性を使用することを推奨する主な理由は、マーカーインターフェイスを削除すると、クラスライブラリの継承構造がはるかに扱いやすくなるためです。15のタイプと2つの階層の階層を持つクラス図と比較して、30のタイプと6つの階層の継承階層を持つクラスダイアグラムは非常に困難です。

実際にAPIを使用している何百万人もの開発者がいる場合、またはコードベースが非常に大きい場合(たとえば、LOCが10万を超える場合)、これらのガイドラインに従うことは非常に役立ちます。

500万人の開発者がAPIの学習に60分を費やすのではなく15分を費やすと、結果として428人年の正味の節約になります。それは長い時間です。

ただし、ほとんどのプロジェクトには、何百万人もの開発者や10万以上のLOCは含まれません。典型的なプロジェクトでは、たとえば4人の開発者と約50Kの場所で、想定のセットは大きく異なります。チームの開発者は、コードがどのように機能するかをよりよく理解できます。つまり、高品質のコードを迅速に作成し、バグの量と変更に必要な労力を削減するために最適化する方がはるかに理にかなっています。

.netフレームワークと整合性のあるコードの開発に1週間を費やすのに対し、変更が簡単でバグの少ないコードを8時間で作成すると、次のような結果になります。

  1. 後期プロジェクト
  2. ボーナスを下げる
  3. バグ数の増加
  4. オフィスで過ごす時間が増え、ビーチでマルガリータを飲む時間が減りました。

コストを吸収する他の開発者が4,999,999人いないと、通常、それだけの価値はありません。

たとえば、マーカーインターフェイスのテストは1つの「is」式になり、属性を探すコードが少なくなります。

だから私のアドバイスは:

  1. 広範な消費を対象としたクラスライブラリ(またはUIウィジェット)を開発している場合は、フレームワークのガイドラインに忠実に従ってください。
  2. プロジェクトに100Kを超えるLOCがある場合は、それらのいくつかを採用することを検討してください。
  3. それ以外の場合は完全に無視してください。

12
個人的には、後で使用する必要があるライブラリとして作成したコードをすべて確認しています。消費が広まっているかどうかは特に気にしません-ガイドラインに従うことで一貫性が高まり、コードを見て何年か後に理解する必要がある場合の驚きが減ります...
Reed Copsey

16
ガイドラインが悪いと言っているのではありません。あなたのコードベースのサイズとあなたが持っているユーザーの数に応じて、それらは異なるはずだと私は言っています。設計ガイドラインの多くは、バイナリの比較可能性の維持などに基づいています。これは、BCLのようなものほど、プロジェクトの一部で使用される「内部」ライブラリにとって重要ではありません。ユーザビリティに関連するもののような他のガイドラインはほとんど常に重要です。道徳は、特に小規模なプロジェクトでは、ガイドラインについて過度に信心深くないことです。
Scott Wisniewski、2010

6
+1-OPの質問に完全に回答しなかった-MIの目的-それでも非常に役立つ。
bzarah

5
@ScottWisniewski:重大なポイントが欠けていると思います。フレームワークガイドラインは、大規模なプロジェクトには適用されません。中規模および一部の小規模なプロジェクトに適用されます。Hello-Worldプログラムに常に適用しようとすると、過剰なスキルになります。たとえば、インターフェイスを5つのメソッドに制限することは、アプリのサイズに関係なく、常に優れた経験則です。あなたが見逃しているもう一つのことは、今日の小さなアプリが明日の大きなアプリになるかもしれません。そのため、大規模なアプリに適用される優れた原則に基づいて構築することをお勧めします。そうすることで、スケールアップするときに、多くのコードを書き直す必要がなくなります。
Phil

2
(ほとんどの)設計ガイドラインに従うと、突然1週間かかる8時間のプロジェクトがどのように発生するのか、私にはよくわかりません。例:代わりにvirtual protectedテンプレートメソッドDoSomethingCoreに名前DoSomethingを付けることはそれほど多くの追加作業ではなく、テンプレートメソッドであることを明確に伝えます... IMNSHO、APIを考慮せずにアプリケーションを作成する人々(But.. I'm not a framework developer, I don't care about my API!)は、正確に大量の複製(また、ドキュメント化されておらず、通常は読み取り不能なコードです。その逆ではありません。
ラウジン、2015年

44

マーカーインターフェイスは、クラスの機能を実行時に特定のインターフェイスを実装するものとしてマークするために使用されます。

インターフェイスデザイン.NET型設計ガイドライン-インターフェイスデザインは、 C#の属性を使用しての賛成でマーカー・インターフェースの使用を思いとどまらが、@Jay Bazuziが指摘するように、属性のよりマーカインタフェースをチェックする方が簡単です:o is I

これの代わりに:

public interface IFooAssignable {} 

public class FooAssignableAttribute : IFooAssignable 
{
    ...
}

.NETガイドラインでは、次のことを行うことを推奨しています。

public class FooAssignableAttribute : Attribute 
{
    ...
}

[FooAssignable]
public class Foo 
{    
   ...
} 

26
また、ジェネリックはマーカーインターフェイスでは完全に使用できますが、属性では使用できません。
ジョルダン2010

18
私は属性と宣言的な観点からそれらがどのように見えるかが大好きですが、それらは実行時には一流の市民ではなく、作業するためにかなりの量の比較的低レベルの配管が必要です。
ジェシーC.スライサー、2010

4
@Jordão-これはまさに私の考えでした。例として、データベースアクセスコードを抽象化したい場合(たとえば、LinqからSqlへ)、共通のインターフェイスを使用すると、LOTが簡単になります。実際、属性にキャストすることはできず、ジェネリックでそれらを使用することができないため、属性を使用してそのような抽象化を書くことは可能ではないと思います。他のすべてのクラスの派生元である空の基本クラスを使用することもできると思いますが、それは空のインターフェースを持つのとほぼ同じように感じます。さらに、後で共有機能が必要だとわかった場合でも、メカニズムはすでに整っています。
tandrewnichols

23

他のすべての回答では「回避する必要がある」と述べているため、理由を説明しておくと役立ちます。

まず、マーカーインターフェイスが使用される理由:マーカーインターフェイスは、それを実装するオブジェクトを使用しているコードが、そのインターフェイスを実装しているかどうかを確認し、実装している場合はオブジェクトを異なる方法で処理できるようにするために存在します。

このアプローチの問題は、カプセル化が壊れることです。オブジェクト自体が、外部での使用方法を間接的に制御できるようになりました。さらに、それは使用されるシステムの知識を持っています。マーカーインターフェイスを適用することにより、クラス定義は、マーカーの存在をチェックする場所で使用されることを期待していることを示唆しています。使用環境に関する暗黙の知識があり、使用方法を定義しようとしています。これは、独自のスコープの外に完全に存在するシステムの一部の実装に関する知識を持っているため、カプセル化の概念に反します。

実用レベルでは、これにより移植性と再利用性が低下します。クラスが別のアプリケーションで再利用される場合、インターフェースもコピーする必要があり、新しい環境では意味がなく、完全に冗長になる場合があります。

したがって、「マーカー」はクラスに関するメタデータです。このメタデータはクラス自体では使用されず、オブジェクトを特定の方法で処理できるように、(一部の!)外部クライアントコードに対してのみ意味があります。これはクライアントコードに対してのみ意味があるため、メタデータはクラスAPIではなくクライアントコード内にある必要があります。

「マーカーインターフェース」と通常のインターフェースの違いは、メソッド付きのインターフェースはそれどのように使用できるかを外の世界に伝えるのに対し、空のインターフェースはそれどのように使用されるべきかを外の世界に伝えているということです。


1
インターフェイスの主な目的は、そのインターフェイスに関連付けられたコントラクトを遵守することを約束するクラスと守らないクラスを区別することです。インターフェイスはコントラクトを実行するために必要なメンバーの呼び出し署名も提供しますが、特定のクラスによって特定のインターフェイスを実装する必要があるかどうかを決定するのは、メンバーではなくコントラクトです。のコントラクトIConstructableFromString<T>で、クラスTIConstructableFromString<T>静的メンバーを持っている場合にのみ実装できることを指定している場合
supercat '21

... public static T ProduceFromString(String params);、インターフェイスのコンパニオンクラスはメソッドを提供できますpublic static T ProduceFromString<T>(String params) where T:IConstructableFromString<T>。クライアントコードにのようなメソッドがある場合T[] MakeManyThings<T>() where T:IConstructableFromString<T>、クライアントコードを変更する必要なく、クライアントコードで機能する新しいタイプを定義できます。メタデータがクライアントコードに含まれている場合、既存のクライアントで使用する新しいタイプを作成することはできません。
スーパーキャット2015

しかし、とTそれを使用するクラスの間のコントラクトは、IConstructableFromString<T>いくつかの動作を記述するインターフェイス内にメソッドがあるため、マーカーインターフェイスではありません。
トムB

クラスに必要な静的メソッドは、インターフェースの一部ではありません。インターフェースの静的メンバーは、インターフェース自体によって実装されます。インターフェイスが実装クラスの静的メンバーを参照する方法はありません。
スーパーキャット

メソッドがリフレクションを使用して、ジェネリック型に特定の静的メソッドがあるかどうかを判断し、そのメソッドが存在する場合はそれを実行することは可能ですが、ProduceFromString上記の例で静的メソッドを検索して実行する実際のプロセスには、必要な機能を実装するために期待されるクラスを示すマ​​ーカーとして使用されることを除いて、インターフェースは何らかの方法で使用されます。
スーパーキャット

8

言語が差別的な和集合をサポートしていない場合、マーカーインターフェイスが必要な悪になる場合があります体型をます。

型がA、B、またはCのいずれかでなければならない引数を必要とするメソッドを定義するとします。多くの関数優先言語(F#など)では、そのような型は次のように明確に定義できます。

type Arg = 
    | AArg of A 
    | BArg of B 
    | CArg of C

ただし、C#などのOO優先言語では、これは不可能です。ここで同様のことを行う唯一の方法は、インターフェースIArgを定義し、それを使ってA、B、Cに「マーク」を付けることです。

もちろん、単に「オブジェクト」型を引数として受け入れることでマーカーインターフェイスの使用を回避できますが、表現力とある程度の型の安全性が失われます。

識別された共用体型は非常に有用であり、少なくとも30年間関数型言語で存在しています。奇妙なことに、今日まで、すべての主流のOO言語はこの機能を無視しています。ただし、実際には関数型プログラミング自体とは関係ありませんが、型システムに属しています。


ので、ことは注目にそれの価値Foo<T>あらゆるタイプの静的フィールドの別のセットを持ってT、それは一般的なクラスを持って処理するためのデリゲートを含む静的フィールドを含むことは難しいことではありませんT、そしてクラスがあるすべてのタイプを処理するための機能で、これらのフィールドに事前で動作するはずです。型にジェネリックインターフェイス制約を使用するとT、コンパイラー時に、提供された型が有効であると少なくとも主張していることを確認します。
スーパーキャット2013

6

マーカーインターフェイスは、空のインターフェイスにすぎません。クラスは、何らかの理由で使用されるメタデータとしてこのインターフェイスを実装します。C#では、他の言語でマーカーインターフェイスを使用するのと同じ理由で、より一般的に属性を使用してクラスをマークアップします。


4

マーカーインターフェイスを使用すると、すべての子孫クラスに適用される方法でクラスにタグを付けることができます。「純粋な」マーカーインターフェイスは、何も定義または継承しません。より有用なタイプのマーカーインターフェイスは、別のインターフェイスを "継承"するものの、新しいメンバーを定義しないタイプです。たとえば、「IReadableFoo」というインターフェースがある場合、「IImmutableFoo」というインターフェースを定義することもできます。これは「Foo」のように動作しますが、それを使用する人は誰もその値を変更しないと約束します。IImmutableFooを受け入れるルーチンは、IReadableFooと同様にそれを使用できますが、ルーチンはIImmutableFooの実装として宣言されたクラスのみを受け入れます。

「純粋な」マーカーインターフェースの多くの用途は考えられません。タイプがIEqualityComparerも実装している場合でも、IDoNotUseEqualityComparerを実装したすべてのタイプのEqualityComparer(of T).DefaultがObject.Equalsを返すかどうかは、私が考えることができる唯一のものです。これにより、Liskov置換の原則に違反することなく、封印されていない不変の型を持つことができます。型が等価性テストに関連するすべてのメソッドを封印している場合、派生型はフィールドを追加して変更可能にすることができますが、そのようなフィールドの変更はできませんベースタイプのメソッドを使用して表示されます。封印されていない不変のクラスがあり、EqualityComparer.Defaultの使用を回避するか、派生クラスを信頼してIEqualityComparerを実装しないことは、恐ろしいことではありません。


4

これら2つの拡張メソッドは、スコットが属性よりもマーカーインターフェイスを支持することを主張するほとんどの問題を解決します。

public static bool HasAttribute<T>(this ICustomAttributeProvider self)
    where T : Attribute
{
    return self.GetCustomAttributes(true).Any(o => o is T);
}

public static bool HasAttribute<T>(this object self)
    where T : Attribute
{
    return self != null && self.GetType().HasAttribute<T>()
}

今あなたは持っています:

if (o.HasAttribute<FooAssignableAttribute>())
{
    //...
}

対:

if (o is IFooAssignable)
{
    //...
}

Scottが主張しているように、APIの構築に2番目のパターンと比較して最初のパターンの5倍の時間がかかるかどうかはわかりません。


1
まだジェネリックはありません。
Ian Kemp

0

マーカーは空のインターフェースです。マーカーはそこにあるか、ありません。

クラスFoo:IConfidential

ここでは、Fooを機密としてマークします。実際の追加のプロパティや属性は必要ありません。


0

マーカーインターフェイスは、本体/データメンバー/実装のない完全な空白のインターフェイスです。
クラスは必要に応じてマーカーインターフェイスを実装します。これは単に「マーク」することです。これは、特定のクラスが複製を目的としていることをJVMに伝え、複製を許可します。この特定のクラスはオブジェクトをシリアル化するためのものなので、オブジェクトのシリアル化を許可してください。


0

マーカーインターフェイスは、オブジェクト指向言語での手続き型プログラミングにすぎません。マーカーインターフェイスはそれ自体しか定義しないため、インターフェイスは、マーカーインターフェイスを除いて、実装者とコンシューマー間のコントラクトを定義します。そのため、ゲートのすぐ外では、マーカーインターフェイスはインターフェイスであるという基本的な目的で失敗します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.