「必要なものだけを要求する」インターフェースの原則はありますか?


9

基本的に「必要なものだけを尋ねる」というインターフェースの設計と消費の原則を使用するようになりました。

たとえば、削除できるタイプがたくさんある場合は、Deletableインターフェイスを作成します。

interface Deletable {
   void delete();
}

次に、ジェネリッククラスを記述できます。

class Deleter<T extends Deletable> {
   void delete(T t) {
      t.delete();
   }
}

コードの他の場所では、クライアントコードのニーズを満たすために可能な限り小さな責任を常に求めます。したがって、を削除するだけでよい場合Fileでも、Deletableではなくを要求しますFile

この原則は常識であり、すでに受け入れられている名前ですか?物議を醸していますか?それは教科書で議論されていますか?


1
たぶん疎結合?または狭いインターフェース?
tdammers 2012

回答:


16

これは、Robert Martinがインターフェイス分離原則と呼んでいるものを指していると思います。インターフェイスは小さく簡潔なものに分かれているため、コンシューマ(クライアント)は、関心のあるメソッドについてのみ知る必要があります。SOLIDで詳細を確認できます。


4

ヴァディムの非常に良い答えをさらに詳しく説明するために、私は「議論の余地があるか」という質問に「いいえ、本当ではない」と答えます。

一般に、関係するさまざまなオブジェクトの「変更する理由」の総数を減らすことにより、インターフェースの分離は良いことです。コアとなる原則は、複数のメソッドを持つインターフェースを変更する必要がある場合、たとえばインターフェースメソッドの1つにパラメーターを追加する場合、変更されたメソッドを使用していなくても、インターフェースのすべてのコンシューマーを少なくとも再コンパイルする必要があります。。「しかし、それは単なる再コンパイルです!」と私はあなたが言うのを聞きます。それは本当かもしれませんが、通常、バイナリの変更がどれほど重要であっても、再コンパイルしたものはすべてソフトウェアパッチの一部としてプッシュする必要があることに注意してください。これらのルールはもともと90年代前半に概念化されたもので、平均的なデスクトップワークステーションはポケットに入る電話よりも強力でなく、14.4kボーのダイヤルアップは無意味で、3.5 "1.44MB"フロッピー "が主要なリムーバブルメディアでした。 3G / 4Gの現在の時代でさえ、無線インターネットユーザーはデータプランに制限があることが多いため、アップグレードをリリースするときは、ダウンロードする必要があるバイナリが少ないほど良いです。

ただし、すべての優れたアイデアと同様に、不適切に実装すると、インターフェースの分離が悪くなる可能性があります。まず、これらのインターフェースを実装するオブジェクト(依存関係を満たす)を比較的変更せずにインターフェースを分離することで、「Hydra」、つまり「God Object」アンチパターンの親戚になってしまう可能性があります。オブジェクトのすべてを知っている、すべての強力な性質は、狭いインターフェースによって依存から隠されます。あなたは、少なくとも神オブジェクトと同じくらい維持するのが難しい多頭の怪物に加えて、そのすべてのインターフェースを維持するオーバーヘッドをもたらします。超えてはならないインターフェースの数はそれほど多くありませんが、単一のオブジェクトに実装する各インターフェースの前には、「このインターフェースはオブジェクトに寄与するか」という質問に答える必要があります。

第2に、SRPが通知する内容にかかわらず、メソッドごとのインターフェイスは必要ない場合があります。あなたは「ラビオリコード」になるかもしれません。一口サイズのチャンクが非常に多いため、実際にどこで起こっているかを正確に見つけるためにトレースするのは困難です。また、そのインターフェイスの現在のすべてのユーザーが両方のメソッドを必要とする場合、2つのメソッドでインターフェイスを分割する必要もありません。依存するクラスの1つが2つのメソッドのいずれか1つだけを必要とする場合でも、そのメソッドの概念の凝集度が非常に高い場合は、インターフェイスを分割しないことが一般的に許容されます(良い例は、互いに正反対の「匿名メソッド」です)。

インターフェイスの分離は、インターフェイスに依存するクラスに基づいている必要があります。

  • インターフェイスに依存するクラスが1つしかない場合は、分離しないでください。クラスが1つ以上のインターフェースメソッドを使用せず、それがインターフェースの唯一のコンシューマーである場合、最初にこれらのメソッドを公開してはならない可能性があります。

  • インターフェイスに依存するクラスが複数あり、すべての依存クラスがインターフェイスのすべてのメソッドを使用する場合は、分離しないでください。インターフェースを変更する必要がある場合(メソッドを追加するか、シグニチャーを変更する場合)、分離するかどうかに関係なく、現在のすべてのコンシューマーが変更の影響を受けます(ただし、少なくとも1つの依存関係が不要なメソッドを追加する場合は、変更を代わりに新しいインターフェースとして実装する必要がある場合は、既存のインターフェースを継承する可能性があるので注意してください)。

  • インターフェースに依存するクラスが複数あり、それらがすべて同じメソッドを使用しない場合、それは分離の候補です。インターフェースの「一貫性」を見てください。すべてのメソッドは、単一の非常に具体的なプログラミング目標をさらに進めますか?インターフェース(およびそのインプリメンター)の複数のコア目的を特定できる場合は、それらの線に沿ってインターフェースを分割して、「変更する理由」の少ない、より小さいインターフェースを作成することを検討してください。


また、コードでインターフェイスの正確な組み合わせを指定できるOOP言語/システムを使用している場合は、インターフェイスの分離が問題なく行われる可能性がありますが、少なくとも.NETでは、まともなものがないため、深刻な問題が発生する可能性があります。 「IFooとIBarを実装するものの、共通点がないもの」のコレクションを指定する方法。
スーパーキャット2014

ジェネリック型パラメーターは、複数のインターフェイスの実装を含む基準を使用して定義できますが、静的型を必要とする式は通常、複数の指定をサポートできないというのはあなたの言う通りです。静的型がIFooとIBarの両方を実装する必要があり、これらの両方のインターフェイスを制御する場合IBaz : IFoo, IBarは、代わりに実装して要求することをお勧めします。
KeithS 2014年

クライアントコードでIFooand として使用できるものが必要な場合はIBar、コンポジットを定義するIFooBarことをお勧めしますが、インターフェースが細かく分割されている場合、何十もの異なるインターフェースタイプが必要になることは簡単です。列挙、レポート数、n番目の要素の読み取り、n番目の要素の書き込み、n番目の要素の前に挿入、n番目の要素の削除、新しいアイテム(コレクションを拡大して新しいスペースのインデックスを返す)、および追加の機能について考えます。9つの方法:ECRWIDNA。おそらく、多くのさまざまな組み合わせを自然にサポートする数十のタイプを説明できます。
スーパーキャット2014年

たとえば、配列はECRWをサポートします。arraylistはECRWIDNAをサポートします。スレッドセーフリストはECRWNAをサポートしている可能性があります(ただし、Aは通常、リストの事前入力にのみ役立ちます)。読み取り専用の配列ラッパーはECRをサポートする場合があります。共変リストインターフェイスはECRDをサポートできます。非ジェネリックインターフェイスは、タイプセーフサポートCまたはCDを提供できます。スワップがオプションである場合、一部のタイプはCSをサポートできますが、D(例:アレイ)はサポートできませんが、他のタイプはCDSをサポートします。必要な能力の組み合わせごとに異なるインターフェースタイプを定義しようとすると、悪夢になります。
スーパーキャット2014年

ここで、コレクションが実行できるすべてのことを実行できるが、すべてのトランザクションをログに記録するオブジェクトでコレクションをラップする機能が必要だと想像してください。ラッパーはいくつ必要ですか?すべてのコレクションが、それらの機能を識別するプロパティを含む共通のインターフェースから継承した場合、1つのラッパーで十分です。ただし、すべてのインターフェースが異なる場合は、数十が必要になります。
スーパーキャット2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.