web-dev-qa-db-ja.com

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

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

84
coder

これは、「Mitch Wheat」による応答に基づいた少しの接線です。

一般的に、人々がフレームワークの設計ガイドラインを引用するのを見るたびに、私はいつもそれを言及したいです:

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

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

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

その中の多くの提案は、あなたがすることをするためにあなたを導くことができます:

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

.netフレームワークは大きく、本当に大きいです。それは非常に大きいため、だれもがそのすべての側面について詳細な知識を持っていると想定することは絶対に不合理です。実際、ほとんどのプログラマーは、以前に使用したことのないフレームワークの部分に頻繁に出会うと想定する方がはるかに安全です。

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

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

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

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

これらのガイドラインがマーカーインターフェイスの代わりに属性を使用することを提案する主な理由は、マーカーインターフェイスを削除すると、クラスライブラリの継承構造がより親しみやすくなるためです。 30種類と6層の継承階層を持つクラス図は、15種類と2層の階層を持つクラス図と比較すると非常に困難です。

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

500万人の開発者がAPIの学習に60分を費やすのではなく、15分間のAPIの学習に費やすと、428人年の純節約になります。それは多くの時間です。

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

.netフレームワークと整合性のあるコードの開発に1週間を費やすのに対して、変更が容易でバグの少ないコードを書くのに8時間かかります。

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

コストを吸収する他の開発者4,999,999なしでは、通常は価値がありません。

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

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

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

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

Interface Design および 。NET Type Design Guidelines-Interface Design は、C#で属性を使用することを優先してマーカーインターフェイスの使用を推奨しませんが、@ Jay Bazuziが指摘するように、属性よりマーカーインターフェイスの方が簡単に確認できます:o is I

したがって、これの代わりに:

public interface IFooAssignable {} 

public class FooAssignableAttribute : IFooAssignable 
{
    ...
}

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

public class FooAssignableAttribute : Attribute 
{
    ...
}

[FooAssignable]
public class Foo 
{    
   ...
} 
44
Mitch Wheat

他のすべての回答には「避けるべき」と記載されているため、理由を説明しておくと便利です。

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

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

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

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

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

22
Tom B

マーカーインターフェイスは、言語が識別されたunionタイプをサポートしていない場合、必要な悪である場合があります。

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

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

ただし、C#などのオブジェクト指向言語では、これは不可能です。ここで似たようなことを実現する唯一の方法は、IArgインターフェースを定義し、A、B、Cに「マーク」することです。

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

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

7
Marc Sigrist

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

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

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倍の時間がかかることはわかりません。

4
Mr Anderson

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

「純粋な」マーカーインターフェイスの多くの用途を考えることはできません。私が考えることができるのは、タイプがIEqualityComparerも実装していても、EqualityComparer(of T).DefaultがIDoNotUseEqualityComparerを実装した任意のタイプのObject.Equalsを返す場合だけです。これにより、Liskov Substitution Principleに違反することなく、封印されていない不変型を持つことができます:型が同等性テストに関連するすべてのメソッドを封印する場合、派生型は追加のフィールドを追加し、それらを可変にすることができますが、そのようなフィールドの突然変異はt基本型のメソッドを使用して表示されます。封印されていない不変クラスを持ち、IEqualityComparerを実装しないためにEqualityComparer.Defaultまたは信頼派生クラスの使用を避けることは恐ろしいことではありませんが、クラスオブジェクト。

4
supercat

マーカーインターフェースは、実際にはOO言語での手続き型プログラミングです。ゲートの外では、マーカーインターフェイスは、インターフェイスであるという基本的な目的で失敗します。

1
Ali Bayat

マーカーインターフェイスは、ボディ/データメンバー/実装のない、完全に空白のインターフェイスです。
クラスimplementsマーカーインターフェイスは、必要に応じて、「mark ";特定のクラスがクローンを作成するためのものであることをJVMに伝えるため、クローンを許可します。この特定のクラスはオブジェクトをシリアル化するため、オブジェクトのシリアル化を許可してください。

0
Arun Raaj

マーカーは空のインターフェイスです。マーカーが存在するか、存在しないかのどちらかです。

クラスFoo:IConfidential

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

0
Rick O'Shea