基本クラスと同じファイルでインターフェイスを宣言するのは良い習慣ですか?


29

交換可能かつテスト可能であるためには、通常、ロジックを備えたサービスにインターフェースが必要です。

public class FooService: IFooService 
{ ... }

設計面ではこれに同意しますが、このアプローチで気になる点の1つは、1つのサービスに対して2つのもの(クラスとインターフェース)を宣言する必要があり、チームでは通常2つのファイル(1つクラス用とインターフェイス用)。IDE(VS2010)で[定義に移動]を使用すると、実際のクラスではなくインターフェイス(他のクラスがインターフェイスを参照するため)を指すため、ナビゲーションの難しさがもう1つあります。

FooServiceと同じファイルにIFooServiceを記述すると、上記の奇妙さが軽減されると考えていました。結局、IFooServiceとFooServiceは非常に関連しています。これは良い習慣ですか?IFooServiceを独自のファイルに配置する必要がある正当な理由はありますか?


3
特定のインターフェイスの実装が1つしかない場合、そのインターフェイスに対する実際の論理的な必要性はありません。クラスを直接使用しないのはなぜですか?
ジョリスティマーマンズ

11
疎結合とテスト容易性のための@MadKeithV?
ルイスリース

10
@MadKeithV:あなたは正しいです。あなたはIFooServiceに依存しているコードのためのユニットテストを書くときには、あなたは一般的にMockFooService、提供されますされ、そのインタフェースの第2の実現を。
ドク・ブラウン

5
@DocBrown-複数の実装がある場合、コメントは明らかに消えますが、「単一の実装」に直接移動できるというユーティリティも同様です(少なくとも2つあるため)。質問は次のようになります。具体的な実装には、インターフェイスを使用している時点でインターフェイスの説明やドキュメントに含まれてはならない情報は何ですか?
ジョリスティマーマンズ

回答:


24

それは独自のファイルにある必要はありませんが、あなたのチームは標準を決定し、それに固執する必要があります。

また、「定義に移動」でインターフェイスに移動するのは正しいですが、Resharperをインストールしている場合は、そのインターフェイスから派生クラス/インターフェイスのリストを1回クリックするだけで開くため、大したことではありません。そのため、インターフェイスを別のファイルに保存します。

 


5
すべてのIDEが設計に合わせて調整する必要があり、その逆ではないので、この答えに同意しますか?
マルクタニ

1
また、Resharperがなければ、F12とShift + F12はほとんど同じことをします。右クリックすらありません:)(公平を期すために、インターフェイスの直接的な使用のみを指示します。Resharperバージョンが何をするかはわかりません)
ダニエルB

@DanielB:Resharperでは、Alt-Endは実装に進みます(または、使用可能な実装のリストが表示されます)。
StriplingWarrior

@StriplingWarriorその後、それはまさにバニラVSが行うことでもあるようです。F12 =定義に移動し、Shift + F12 =実装に移動します(Resharperがなくても機能すると確信していますが、インストール済みですが、確認するのは困難です)。これは最も便利なナビゲーションショートカットの1つです。ただ広めています。
ダニエルB

@DanielB:Shift-F12のデフォルトは「使用法を見つける」です。jetbrains.com/resharper/webhelp/…–
StriplingWarrior

15

別々のファイルを保持する必要があると思います。あなたが言ったように、アイデアはテスト可能で互換性を保つことです。実装と同じファイルにインターフェースを配置することにより、インターフェースを特定の実装に関連付けています。モックオブジェクトまたは別の実装を作成することにした場合、インターフェイスは論理的にFooServiceから分離されません。


10

SOLIDによると、インターフェイスを作成するだけでなく、別のファイルに配置するだけでなく、別のアセンブリに配置する必要があります。

どうして?アセンブリにコンパイルされるソースファイルを変更するには、アセンブリを再コンパイルする必要があり、アセンブリを変更するには、依存するアセンブリを再コンパイルする必要があるためです。したがって、SOLIDに基づく目的が実装Aを実装Bに置き換えることであり、インターフェイスCに依存するクラスCが違いを知る必要がない場合は、アセンブリをIにする必要があります。その中に変更はありませんので、使用を保護します。

「しかし、それはただの再コンパイルです」私はあなたが抗議するのを聞きます。そうかもしれませんが、スマートフォンアプリでは、ユーザーのデータ帯域幅の方が簡単です。変更された1つのバイナリをダウンロードするか、そのバイナリとそれに依存するコードを含む5つの他のバイナリをダウンロードしますか?すべてのプログラムがLAN上のデスクトップコンピューターで使用されるように書かれているわけではありません。帯域幅とメモリが安価な場合でも、Active Directoryまたは同様のドメイン管理レイヤーを介してLAN全体に簡単にプッシュできるため、小規模なパッチリリースは価値があります。ユーザーは、すべてが再インストールされるのに数分かかるのではなく、次回ログインするときに適用されるのを数秒待つだけです。言うまでもなく、プロジェクトをビルドするときに再コンパイルする必要のあるアセンブリが少ないほど、ビルドは速くなり、

さて、免責事項:これは常に可能または実行可能であるとは限りません。これを行う最も簡単な方法は、集中化された「インターフェース」プロジェクトを作成することです。これには独自の欠点があります。永続化レイヤーまたはアプリの他の主要コンポーネントを再利用する他のアプリでインターフェイスプロジェクトと実装プロジェクトを参照する必要があるため、コードの再利用性が低下します。インターフェイスをより緊密に結合されたアセンブリに分割することでこの問題を克服できますが、アプリ内のプロジェクトが増え、フルビルドが非常に苦痛になります。重要なのはバランスであり、疎結合設計を維持します。通常、必要に応じてファイルを移動できるので、クラスに多くの変更が必要な場合、またはインターフェイスの新しい実装が定期的に必要な場合(おそらく、他のソフトウェアの新しくサポートされているバージョンとインターフェイスするため、


8

なぜ個別のファイルがあるのが不快なのですか?私にとっては、ずっときれいできれいです。ソリューションエクスプローラーで表示するファイルの数を減らしたい場合は、「Interfaces」という名前のサブフォルダーを作成し、IFooServer.csファイルをそこに貼り付けるのが一般的です。

インターフェースが独自のファイルで定義されている理由は、通常クラスが独自のファイルで定義されているのと同じ理由です:論理構造とファイル構造が同じである場合、プロジェクト管理はより簡単になります。 in。これにより、デバッグ時(例外スタックトレースでは通常ファイル番号と行番号が示されます)、またはソースコードをソース管理リポジトリにマージするときに、作業が楽になります。


6

コードファイルに1つのクラスまたは1つのインターフェイスのみを含めることをお勧めします。しかし、これらのコーディング手法は、目的を達成するための手段です。つまり、コードをより適切に構成して作業しやすくすることです。あなたとあなたのチームが、クラスがそれらのインターフェースと一緒に保たれているなら、どうしてもそうすることで、より簡単に作業できるとわかります。

個人的には、インターフェイスを実装するクラスが1つだけの場合(たとえば、あなたの場合)に、同じファイルにインターフェイスとクラスを含めることを好みます。

ナビゲーションに関する問題については、ReSharperを強くお勧めします。特定のインターフェイスメソッドを実装するメソッドに直接ジャンプするための非常に便利なショートカットが含まれています。


3
チームの慣行がファイル構造を決定する必要があることを指摘し、規範に反するアプローチを行ったことに対して+1。

3

インターフェイスをクラスとは別のファイルだけでなく、完全に別のアセンブリにも配置したい場合が多くあります。

たとえば、ワイヤの両端を制御できる場合、クライアントとサービスの両方でWCFサービスコントラクトインターフェイスを共有できます。インターフェイスを独自のアセンブリに移動すると、独自のアセンブリ依存関係が少なくなります。これにより、クライアントが消費しやすくなり、実装との結合が緩みます。


2

これまでならばそれはほとんど、インタフェースの単一の実装持っている意味がありません1。パブリックインターフェイスと、そのインターフェイスを実装するパブリッククラスを同じファイルに配置した場合、インターフェイスが不要になる可能性が高くなります。

あなたがそのクラスのインターフェイスと共同で、見つけたときで抽象的、そしてあなたのインターフェイスのすべての実装は、理にかなって同じファイルに2を見つけ、その抽象クラスを継承する必要があることを知っています。インターフェイスを使用するかどうかの決定を精査する必要があります。抽象クラス自体で問題ないかどうかを自問し、答えが肯定的な場合はインターフェイスを削除します。

ただし、一般的には、「1つのパブリッククラス/インターフェイスが1つのファイルに対応する」戦略に従う必要があります。これは簡単であり、ソースツリーのナビゲートが容易になります。


1重要な例外の1つは、テスト用のインターフェイスが必要な場合です。これは、モックフレームワークを選択すると、この追加要件がコードに追加されるためです。


3
実際、ユニットテストで依存関係をモック、スタブ、スパイ、またはその他のテストダブルに置き換えることができるため、.NETの1つのクラスに1つのインターフェイスを使用することは非常に正常なパターンです。
ピート

2
@Pete回答を編集して、これをメモとして含めました。このパターンを「通常」とは呼びません。なぜなら、テスト可能性の懸念がメインコードベースに「漏れる」からです。最新のモックフレームワークは、この問題を大幅に克服するのに役立ちますが、そこにインターフェイスがあることで、作業が大幅に簡素化されます。
-dasblinkenlight

2
@KeithSインターフェイスのないクラスは、サブクラスを作成しても意味がないと確信していて、そのクラスを作成する場合にのみ、デザインに含める必要がありますsealed。将来、2つ目のサブクラスを追加する可能性があると思われる場合は、すぐにインターフェイスを設定します。PSまともな品質のリファクタリングアシスタントは、1つのReSharperライセンスを
手頃

1
@KeithSそして、はい、サブクラス化のために特別に設計されていないクラスはすべて封印する必要があります。実際、希望クラスはデフォルトで封印されており、明示的に継承のためにクラスを指定することを強制します。これは、言語が現在、それらをマークすることによりオーバーライドする関数を指定するように強制するのと同じ方法ですvirtual
-dasblinkenlight

2
@KeithS:1つの実装が2つになるとどうなりますか?実装が変更されたときと同じです。その変更を反映するようにコードを変更します。YAGNIがここに適用されます。将来何が変わるのかを知ることは決してないので、可能な限り簡単なことをしてください。(残念ながらこの格言で混乱をテストしています)
-Groky

2

Robert C. Martinによるアジャイルの原則、実践、およびパターンで定義されているように、インターフェイスは実装ではなくクライアントに属します。したがって、同じ場所でインターフェイスと実装を組み合わせることは、原則に反します。

クライアントコードはインターフェイスに依存します。これにより、実装せずにクライアントコードとインターフェイスコンパイルおよび展開できます。クライアントコードへのプラグインのように動作するさまざまな実装を持つことができます。

更新:これはアジャイルのみの原則ではありません。94年のデザインパターンのギャングオブフォーは、インターフェイスに固執しているクライアントとインターフェイスへのプログラミングについて既に話し合っています。彼らの見解は似ています。


1
インターフェイスの使用は、アジャイルが所有するドメインをはるかに超えています。アジャイルは、特定の方法で言語構造を使用する開発方法論です。インターフェースは言語構造です。

1
はい。ただし、アジャイルについて話しているかどうかにかかわらず、インターフェイスは実装ではなくクライアントに属します。
パトコスチャバ

これについてはあなたと一緒です。
マルジャンヴェネマ

0

個人的には、インターフェイスの前にある「私」が好きではありません。そのようなタイプのユーザーとして、これがインターフェースであるかどうかを見たくありません。タイプは興味深いものです。依存関係に関して、FooServiceはIFooServiceの1つの可能な実装です。インターフェイスは、使用場所の近くにある必要があります。一方、実装は、クライアントに影響を与えずに簡単に変更できる場所に配置する必要があります。だから、私は2つのファイルを好むだろう-通常。


2
ユーザーとして、型がインターフェイスであるかどうかを知りたいのは、たとえば、それを使用できないことを意味しますが、new一方で、共変または反変で使用できるからです。そして、これIは.Netで確立された命名規則であるため、他のタイプとの一貫性のためだけに、これに従う必要があります。
svick

Iin interface から始めることは、2つのファイルの分離に関する私の議論の紹介に過ぎません。コードは読みやすく、依存関係の反転がより明確になります。
オリンズ

4
好むと好まざるとにかかわらず、インターフェイス名の前に「I」を使用することは、.NETの事実上の標準です。.NETプロジェクトの標準に従わないということは、私の観点では「最小限の驚きの原則」に違反することになります。
ピート

私の主なポイントは、2つのファイルに分けます。1つは抽象化用、もう1つは実装用です。Iご使用の環境での使用が正常な場合:使用してください!(しかし、私はそれが好きではない:))
ollins

また、P.SEの場合、インターフェイスの前にIをプレフィックスすることにより、ポスターが別のクラスを継承せずにインターフェイスについて話していることがより明確になります。

0

インターフェイスへのコーディングは不適切である可能性があり、実際には特定のコンテキストのアンチパターンとして扱われ、法律ではありません。通常、インターフェイスアンチパターンへのコーディングの良い例は、アプリケーションに実装が1つしかないインターフェイスがある場合です。もう1つは、インターフェイスファイルが煩わしくなり、実装されたクラスのファイルでその宣言を非表示にする必要がある場合です。


インターフェイスのアプリケーションに実装が1つしかないことは、必ずしも悪いことではありません。技術の変化からアプリケーションを保護することもできます。たとえば、データ永続化テクノロジのインターフェイス。現在のデータ永続化テクノロジの実装は1つしかなく、変更する場合はアプリを保護します。したがって、その時点では1つのインプしかありません。
TSmith

0

クラスとインターフェースを同じファイルに入れても、どちらを使用するかに制限はありません。インターフェースは、実装するクラスと同じファイル内にある場合でも、モックなどに同じように使用できます。質問は純粋に組織的な利便性の1つとして捉えることができます。その場合、私はあなたの人生を楽にするものなら何でもします。

もちろん、2つを同じファイルに含めることで、実際よりもプログラム的に結合されていると不注意に思われる可能性があり、これはリスクです。

個人的には、一方の規約をもう一方の規約よりも使用しているコードベースに出会ったとしても、どちらの方法でも心配する必要はありません。


「組織の利便性」だけではありません...リスク管理、展開管理、コードトレーサビリティ、変更管理、ソース管理ノイズ、セキュリティ管理の問題でもあります。この質問には多くの角度があります。
TSmith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.