web-dev-qa-db-ja.com

インターフェイスを使用するタイミング(単体テスト、IoC?)

私はここで男子生徒の誤りを犯したと思われ、説明を求めています。私のソリューション(C#)のクラスの多く-あえて過半数を言います-対応するインターフェースを作成してしまいました。例えば。 「ICalculator」インターフェースとそれを実装する「Calculator」クラスですが、その計算機を別の実装に置き換えることはほとんどありません。また、これらのクラスのほとんどは、依存関係と同じプロジェクトにあります。実際に必要なのはinternalだけですが、それぞれのインターフェースを実装することによる副作用として、publicになっています。

私はすべてのためのインターフェースを作成するこの習慣はいくつかの虚偽から生じたと思います:-

1)最初はユニットテストモックを作成するためにインターフェイスが必要だと思っていました(私はMoqを使用しています)。そのメンバーがvirtualであり、それがパラメータなしのコンストラクタ(間違っている場合は修正してください)。

2)IoCフレームワーク(Castle Windsor)にクラスを登録するにはインターフェースが必要であると当初考えていました。

Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...

実際、私はそれ自体に対して具体的な型を登録することができました:

Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...

3)インターフェースの使用。依存性注入のコンストラクタパラメータ。結果は「疎結合」になります。

だから私はインターフェースに夢中になっていますか?!私は、「通常」インターフェースを使用するシナリオを知っています。公開APIの公開、または「プラグ可能な」機能のようなもの。私のソリューションには、このようなユースケースに適合する少数のクラスがありますが、他のすべてのインターフェイスは不要であり、削除する必要があるのでしょうか。上記の3)に関して、私がこれを行おうとした場合、「疎結合」に違反しませんか?

編集:-私はMoqで遊んでいるだけで、publicおよびvirtualであるメソッドが必要なようです。それらをモックできるように、publicパラメータのないコンストラクタを持っています。それで、内部クラスを持つことができないように見えますか?

17
Andrew Stephens

一般に、実装者が1人だけのインターフェースを作成すると、2回書くだけで時間を浪費することになります。インターフェイスが1つの実装に密接に結合されている場合、インターフェースだけでは疎結合は提供されません...つまり、これらを単体テストで模擬したい場合、通常、実際には複数の実装者が必然的に必要になるという大きな兆候です。コード。

ほとんどすべてのクラスにインターフェイスがある場合は、少しコードのにおいだと思います。つまり、ほぼすべてが何らかの方法で互いに連携しているということです。 (ヘルパークラスがないため)クラスの処理が多すぎるか、抽象化が多すぎると思います(ああ、変更される可能性があるため、gravityのプロバイダーインターフェイスが必要です!)。

7
Telastyn

1)具象クラスがモック可能である場合でも、メンバーをvirtualにし、パラメーターのないコンストラクターを提供する必要があります。あなた(または新しいチームメンバー)は、「すべてが機能する」という理由だけで、すべての新しいクラスにvirtualとパラメーターなしのコンストラクターを考え直すことなく、体系的に追加します。

インターフェースは、依存関係をモックする必要がある場合のIMOのはるかに優れたアイデアです。これにより、本当に必要になるまで実装を延期でき、依存関係の明確なコントラクトが実現します。

3)それはどのようにして虚偽ですか?

インターフェースは、コラボレーションしているオブジェクト間のメッセージングプロトコルを定義するのに最適だと思います。たとえば ドメインエンティティ のように、それらの必要性が議論される場合があります。ただし、通信する必要のある安定したパートナー(つまり、一時的な参照ではなく注入された依存関係)がある場合は、通常、インターフェイスの使用を検討する必要があります。

4
guillaume31

IoCのアイデアは、具象型を置き換え可能にすることです。したがって、(現時点では)ICalculatorを実装する計算機が1つしかない場合でも、2番目の実装はインターフェースにのみ依存し、Calculatorからの実装の詳細には依存しません。

したがって、IoC-Container登録の最初のバージョンは正しいものであり、今後使用する必要があります。

クラスをパブリックまたは内部にする場合、実際には関係がなく、moq-ingも関係ありません。

3
Wilbert

プロジェクト全体のほぼすべてのタイプを「模擬」する必要性を感じているように見えるという事実については、本当にもっと心配になります。

テストdoubleは、コードを外部の世界(サードパーティのライブラリ、ディスクIO、ネットワークIOなど)または本当に高価な計算から分離するのに最も役立ちます。分離フレームワークの自由度が高すぎると(本当にそれが必要ですか?プロジェクトは複雑ですか?)、実装に結合されたテストが簡単に発生し、設計がより堅固になります。いくつかのクラスがメソッドXとYをこの順序で呼び出したことを確認する一連のテストを記述し、パラメーターa、b、cをメソッドZに渡した場合、本当に値を取得していますか?ロギングやサードパーティのライブラリとの相互作用をテストするときに、このようなテストを受けることがありますが、それは例外であり、ルールではありません。

分離フレームワークから、ものをパブリックにする必要があり、常にパラメーターなしのコンストラクターが必要であることが通知されたら、この時点でフレームワークが悪い(悪い)コードを書くように強制しているため、回避する必要があります。

インターフェースについてより具体的に言えば、インターフェースはいくつかの重要なケースで役立ちます。

  • メソッド呼び出しをさまざまなタイプにディスパッチする必要がある場合。複数の実装がある場合(これは明らかなものです。ほとんどの言語でのインターフェイスの定義は仮想メソッドのコレクションにすぎないためです)

  • コードの残りの部分をクラスから分離できるようにする必要がある場合。これは、アプリケーションのコアロジックからファイルシステム、ネットワークアクセス、データベースなどを抽象化する通常の方法です。

  • アプリケーションの境界を保護するために制御の反転が必要な場合。これは、依存関係を管理するための最も強力な方法の1つです。高レベルモジュールと低レベルモジュールは互いに異なる理由で変化するため、ドメインモデルのHttpContextや、行列乗算ライブラリの一部のPdfレンダリングフレームワークに依存したくないので、高レベルモジュールと低レベルモジュールはお互いについて知らないはずです。 。境界クラスがインターフェースを実装するようにすると(決して置き換えられない場合でも)、レイヤー間の結合が緩くなり、依存関係が他の多くのタイプを知っているタイプからレイヤーを突き抜けるリスクが大幅に減少します。

2
sara

私は、一部のロジックがプライベートである場合、そのロジックの実装は誰にとっても問題にならず、テストするべきではないという考えに傾いています。テストしたいほど複雑なロジックがある場合、そのロジックはおそらく異なる役割を持っているため、公開する必要があります。例えば。プライベートヘルパーメソッドでコードを抽出すると、通常、それは別のパブリッククラス(フォーマッター、パーサー、マッパーなど)に配置できるものであることがわかります。
インターフェイスについては、クラスをスタブまたはモックする必要があるので、私を動かしました。リポジトリのようなもの、私は通常、スタブまたはモックできるようになりたいです。統合テストのマッパー、(通常)それほど多くありません。

1
Stefan Billiet