分類のみにインターフェースを使用するのは悪い習慣ですか?


12

例えば:

私はクラスを持っていると言いますABC。私は2つのインタフェースを持って、それらを呼び出すことができますIAnimalし、IDogIDogから継承しIAnimalます。Aand BIDogsで、while Cはそうではありませんが、IAnimalです。

重要な部分は、IDog追加機能を提供しないことです。特定のメソッドへの引数として渡されることを許可するためだけに使用されますがABではありませんC

これは悪い習慣ですか?


8
IAnimalそして、IDogしているひどいトートロジー名!

5
@JarrodRobersonは同意する傾向がありますが、.Netの世界で仕事をしているなら、あなたはそれに固執しています(あるいは、違うやり方をすると確立された慣習に反するでしょう)。
ダニエルB

2
@JarrodRoberson私見MyProject.Data.IAnimalとは、MyProject.Data.Animalより優れているMyProject.Data.Interfaces.AnimalMyProject.Data.Implementations.Animal
マイクKoder

1
ポイントは、それがInterfaceor Implementationであるかどうかさえ気にする必要はないということです。繰り返しの接頭辞であれ名前空間であれ、いずれにせよトートロジーであり、DRYに違反します。Dog気にする必要があるのはこれだけです。PitBull extends Dog実装の冗長性も必要ありません。単語extendsは私が知る必要があることをすべて教えてくれます。元のコメントに投稿したリンクを読んでください。

1
@ジャロッド:私に文句を言うのを止めてください。.NET開発チームに苦情を申し立てます。私が標準に反した場合、仲間の開発者は私の勇気を嫌うでしょう。
ケンドールフレイ

回答:


13

メンバーを持たないインターフェイス、またはマーカーインターフェイスと呼ばれる同じインターフェイスから継承される別のインターフェイスとまったく同じメンバーを持つインターフェイスで、それをマーカーとして使用しています。

悪い習慣ではありませんが、使用している言語がサポートしている場合、インターフェースを属性(注釈)に置き換えることができます。

Orchard Projectで頻繁に使用される「Marker Interface」パターンを見たので、悪い習慣ではないと自信を持って言うことができます

Orchardプロジェクトのサンプルを次に示します。

public interface ISingletonDependency : IDependency {}

/// <summary>
/// Base interface for services that may *only* be instantiated in a unit of work.
/// This interface is used to guarantee they are not accidentally referenced by a singleton dependency.
/// </summary>
public interface IUnitOfWorkDependency : IDependency {}

/// <summary>
/// Base interface for services that are instantiated per usage.
/// </summary>
public interface ITransientDependency : IDependency {}

Marker Interfaceを参照してください。


属性/注釈はコンパイル時のチェックをサポートしますか(参照言語としてC#を使用)?オブジェクトに属性がない場合は例外をスローできますが、インターフェイスパターンはコンパイル時にエラーをキャッチします。
ケンドールフレイ

マーカーインターフェイスを使用しているため、コンパイルチェックのメリットがまったくない場合。基本的に、インターフェイスごとに型チェックを行っています。
-Freshblood

よくわかりません。この 'Marker interface'パターンは、Cを期待するメソッドにを渡すことができないという点で、コンパイル時のチェックを提供しますIDog
ケンドールフレイ

このパターンを使用しているので、リフレクションジョブを高度に実行しています。型チェックを動的または静的に実行しているということです。私はあなたのユースケースに何か間違いがあると確信していますが、それをどのように使用しているか見ていません。
フレッシュブラッド

テラスティンの答えについてのコメントで述べたようにそれを説明させてください。たとえばKennel、のリストを保存するクラスがありIDogます。
ケンドールフレイ

4

はい、それはほとんどすべての言語で悪い習慣です。

データをエンコードするための型はありません。それらは、データが行きたい場所に行き、振る舞う必要があるように振る舞うことを証明する助けとなります。サブタイプを設定する唯一の理由は、ポリモーフィックな動作が変更された場合です(常にそうではありません)。

タイピングがないので、IDogができることは暗示されています。あるプログラマーはあることを考え、別のプログラマーは別のことを考え、物事は崩壊します。または、よりきめ細かい制御が必要になると、それが表すものが増えます。

[編集:明確化]

つまり、インターフェイスに基づいて何らかのロジックを実行しているということです。一部の方法は犬と他の動物でしか機能しないのはなぜですか?違いを提供する実際のメンバーがないため、その違いは暗黙の契約です。システムに実際のデータはありません。唯一の違いはインターフェイスです(したがって、「入力しない」コメント)。その意味はプログラマによって異なります。[/編集]

特定の言語は、これがそれほど悪くない特性メカニズムを提供しますが、洗練ではなく機能に焦点を合わせる傾向があります。私は彼らとはほとんど経験がないので、そこでのベストプラクティスについて話すことはできません。C ++ / Java / C#では...良くありません。


私はあなたの真ん中の2つの段落に混乱しています。「サブタイプ」とはどういう意味ですか?実装ではなくコントラクトであるインターフェイスを使用していますが、コメントがどのように適用されるかわかりません。「入力なし」とはどういう意味ですか?「暗黙」とはどういう意味ですか?IDogはIAnimalができることをすべて実行できますが、それ以上のことができるとは限りません。
ケンドールフレイ

説明をありがとう。私の特定のケースでは、インターフェイスを異なる方法で使用しているわけではありません。sのKennelリストを格納するクラスがあると言うことで、おそらく最もよく説明できIDogます。
ケンドールフレイ

@KendallFrey:インターフェースからキャストしている(悪い)か、誤った抽象化の匂いがする人為的な区別を作成しています。
テラスティン

2
@Telastyn-インターフェイスなどの目的は、メタデータを提供すること
です-Freshblood

1
@Telastyn:では、属性はコンパイル時のチェックにどのように適用されますか?AFAIK、C#では、属性は実行時にのみ使用できます。
ケンドールフレイ

2

私はC#のみを知っているので、一般的なオブジェクト指向プログラミングではこれを言えませんが、C#の例として、クラスを分類する必要がある場合、明らかなオプションはインターフェイス、列挙型プロパティ、またはカスタム属性です。通常、他の人が何を考えているかに関係なく、インターフェイスを選択します。

if(animal is IDog)
{
}
if(animal.Type == AnimalType.Dog)
{
}
if(animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
{
}


var dogs1 = animals.OfType<IDog>();
var dogs2 = animals.Where(a => a.Type == AnimalType.Dog);
var dogs3 = animals.Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any());


var dogsFromDb1 = session.Query<IDog>();
var dogsFromDb2 = session.Query<Animal>().Where(a => a.Type == AnimalType.Dog);
var dogsFromDb3 = session.Query<Animal>().Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute),false).Any());


public void DoSomething(IDog dog)
{
}
public void DoSomething(Animal animal)
{
    if(animal.Type != AnimalType.Dog)
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
public void DoSomething(Animal animal)
{
    if(!animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}

コードの最後のセクションは、ほとんどこれを使用するためのものです。インターフェイスのバージョンは明らかに短いだけでなく、コンパイル時にチェックされていることがわかります。
ケンドールフレイ

私は似たようなユースケースを持っていますが、ほとんどの.net開発者はそれに対して強く助言しますが、属性を使用するつもりはありません
-GorillaApe

-1

後者のインターフェースのすべての実装が前者のいくつかの実装ではできないことを有用に行えると予想される場合、新しいメンバーを追加せずに別のインターフェースを継承するインターフェースを持つことには何の問題もありません。確かに、より派生したインターフェイスによって暗示される制約をクラスが自然に満たす実装者は、より派生したインターフェイスを単に実装することで示すことができるため、。 、メンバー実装コードまたは宣言を変更する必要はありません。

たとえば、インターフェイスがあるとします:

インターフェースIMaybeMutableFoo {
  Bar Thing {get; set;} //その他のプロパティも
  bool IsMutable;
  IImmutableFoo AsImmutable();
}
インターフェースIImmutableFoo:IMaybeMutableFoo {};

の実装はIImmutableFoo.AsImmutable()それ自体を返すことが期待されます。実装可能な可変クラスIMaybeMutableFoo.AsImmutable()は、データのスナップショットを含む新しい深く不変のオブジェクトを返すことが期待されます。に保持されているデータのスナップショットを保存するルーチンは、IMaybeMutableFooオーバーロードを提供する可能性があります。

public void StoreData(IImmutableFoo ThingToStore)
{
  // ThingToStoreは不変であることが知られているため、参照を保存するだけで十分です。
}
public void StoreData(IMaybeMutableFoo ThingToStore)
{
  StoreData(ThingToStore.AsImmutable()); //より具体的なオーバーロードを呼び出します
}

コンパイラーは、保存されるものがであることを知らない場合、IImmutableFooを呼び出しますAsImmutable()。アイテムが既に不変である場合、関数はすぐに戻りますが、インターフェイスを介した関数呼び出しにはまだ時間がかかります。コンパイラは、アイテムが既にであることを知っている場合、IImmutableFoo不要な関数呼び出しをスキップできます。


Downvoter:コメントしてください。新しいものを追加せずにインターフェイスを継承するのはばかげていることもありますが、それが良い(そしてIMHOが十分に活用されていない)テクニックであることがないという意味ではありません。
-supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.