依存性注入:販売方法[終了]


95

私が依存性注入(DI)と自動テストの大ファンであることを知っておいてください。私はそれについて一日中話すことができました。

バックグラウンド

最近、私たちのチームは、ゼロから構築するこの大きなプロジェクトを手に入れました。複雑なビジネス要件を持つ戦略的アプリケーションです。もちろん、私はそれが素晴らしくてきれいであることを望んでいました。だから私はDIを使いたかった。

抵抗

問題は私たちのチームにありました、DIはタブーです。それは数回育てられましたが、神は承認しません。しかし、それは私を落胆させませんでした。

私の動き

これは奇妙に聞こえるかもしれませんが、サードパーティのライブラリは通常、アーキテクトチームによって承認されていません(「あなたは指を切らないように、「UnityNinjectNHibernateMoqまたはNUnitについて話さないでください」)。そのため、確立されたDIコンテナーを使用する代わりに、非常に単純なコンテナーを作成しました。基本的には、起動時にすべての依存関係を結び付け、依存関係(コンストラクター/プロパティ)を挿入し、Web要求の最後に使い捨てオブジェクトを破棄します。それは非常に軽量で、必要なことだけを行いました。そして、私は彼らにそれをレビューするように頼みました。

応答

まあ、短くするために。私は重い抵抗に会いました。主な議論は、「すでに複雑なプロジェクトにこの複雑さの層を追加する必要はありません」でした。また、「コンポーネントの異なる実装をプラグインするようなものではありません」。そして、「できる限りすべてを1つのアセンブリに詰め込んで、できるだけシンプルにしたいと考えています。DIは、メリットのない不必要な複雑さです」。

最後に、私の質問

私の状況をどのように処理しますか?私は自分のアイデアを提示するのが苦手で、人々が議論をどのように提示するかを知りたいと思います。

もちろん、私と同じように、DIの使用を好むと思います。同意しない場合は、その理由を言ってください。そうすれば、コインの反対側を見ることができます。同意しない人の視点を見るのは本当に面白いでしょう。

更新

みんなの回答ありがとうございます。それは本当に物事を見通しに入れます。フィードバックを提供するために別の目があれば十分ですが、15は本当に素晴らしいです!これは本当に素晴らしい答えであり、さまざまな側面から問題を見るのに役立ちましたが、私は1つの答えしか選択できないので、トップの投票されたものを選ぶだけです。答えてくれてありがとう。

私はおそらくDIを実装するのに最適な時期ではないと判断し、私たちはその準備ができていません。代わりに、設計をテスト可能にし、自動化された単体テストを提示することに努力を集中します。テストを書くことは追加のオーバーヘッドであり、追加のオーバーヘッドがそれだけの価値がないと判断されたとしても、デザインはまだテスト可能であるため、個人的にはそれを勝利の状況と見なします。また、将来的にテストまたはDIを選択する場合、設計で簡単に処理できます。


64
別の会社に移る必要があるように
聞こえ

24
DIの使用は(少なくともJava、C#などでは)単体テストの使用のほぼ自然な結果です。あなたの会社は単体テストを使用していますか?
セバスチャンガイガー

27
または、単にDIに焦点を合わせないでください。DIは「保守可能およびテスト可能」とは異なります。プロジェクトに関して異なる意見がある場合、あなたは作曲しなければならず、時にはあなたが決定しないことを受け入れます。たとえば、制御の反転、DI、およびフレームワークの複数のレイヤーによって災害が増幅されるのを見て、複雑なものをシンプルにする必要があります(あなたの場合は議論できません)。
デニス・Séguret

34
「...それについて一日中話すことができた。」その日のホワイトペーパーを独断的に固守するために、頻繁に乱用されるものに執着するのをやめる必要があるように思えます。

21
その響きから、同僚はDIコンテナの使用に反対する客観的な議論を持っています。「すでに複雑なプロジェクトにこの複雑さの層を追加する必要はありません」-彼らは正しいのでしょうか。やるあなたは追加の複雑さから、実際の利益に?もしそうなら、それは議論することが可能であるべきです。彼らは、「DIは利益を伴わない不必要な複雑さだ」と主張します。客観的な利益を示すことができれば、これを非常に簡単に勝ち取るべきでしょう。テストは他のコメントで頻繁に言及されています。ただし、モックが必要でない場合(これは指定されていません)、テストには基本的にDIは必要ありませ
コンラッドルドルフ

回答:


62

いくつかのカウンター引数を取ります:

「可能であれば、すべてを1つのアセンブリに詰め込むだけです。DIは、メリットのない複雑なものです。」

「コンポーネントの異なる実装をプラグインするのとは異なります」。

必要なのは、システムをテスト可能にすることです。簡単にテスト可能であるためには、プロジェクト(データベース、通信など)の様々な層をからかうと、このケースでは、あなたが見てする必要がありますコンポーネントの異なる実装をプラグインすること。

それがあなたに与えるテストの利点でDIを売ってください。プロジェクトが複雑な場合は、適切で堅実な単体テストが必要になります。

別の利点は、インターフェイスにコーディングしているときに、コンポーネントのいずれかのより良い実装(より速く、より少ないメモリを必要とする)を考え出す場合、DIを使用すると、古い実装をより簡単に交換できることです新着。

ここで私が言っているのは、DIのためにDIを主張するのではなく、DIがもたらす利点に取り組む必要があるということです。人々に声明に同意してもらうことで:

「X、Y、Zが必要です」

その後、問題をシフトします。DIがこのステートメントの答えであることを確認する必要があります。そうすることで、同僚に課せられていると感じるのではなく、同僚がソリューションを所有することになります。


+1。簡潔かつ要点。残念ながら、Melの同僚が提起した議論から、彼は試してみるだけでも価値があると納得させるのに苦労するでしょう。
はPatrykĆwiek

1
@ Trustme-I'maDoctor-それは人の問題だということに同意しますが、このアプローチでは、DI自体を主張するのではなく、利益を示しています。
ChrisF

真実であり、彼の幸運を祈ります。開発者としての彼の人生は、適切なテスト容易性などにより容易になります。)
PatrykĆwiek12年

4
+1最後の段落を最初にすべきだと思います。それが問題の中心です。単体テストが可能になり、システム全体をDIモデルに変換することが提案された場合、彼にもノーと言います。
アンドリューTフィネル

+1とても助かりました。DIは単体テストの絶対的な必須項目ではないとは思いませんが、それは非常に簡単なことです。
メル

56

あなたが書いていることから、DI自体はタブーではなく、DIコンテナの使用です。これは別のことです。コンストラクターなどで渡す必要のある他のコンポーネントを取得する独立したコンポーネントを作成するだけで、チームで問題が発生することはないと思います。「DI」と呼ばないでください。

そのようなコンポーネントがDIコンテナを使用しないと考えるのは退屈であり、あなたは正しいですが、チームにそれを自分で調べる時間を与えてください。


10
政治的/独断的な行き詰まりに対する実用的な回避策を+1
k3b

32
とにかくすべてのオブジェクトを手動で配線することを検討する価値があり、DIコンテナは、毛が生え始めたときにのみ導入します。これを行うと、DIコンテナは複雑さを増すとは見なされません。シンプルさを増すと見なされます。
フィル

2
唯一の問題は、疎結合の依存関係を依存関係に渡すパターン DIであることです。IoC、または「コンテナ」を使用して疎結合の依存関係を解決することは、自動化された DIです。
キース

51

あなたが尋ねたので、私はあなたのチームのDIに反対します。

依存性注入に対するケース

「エネルギーの保存」と呼ばれる物理学のルールに類似した、「複雑さの保存」と呼ぶルールがあります。問題に対する最適なソリューションの全体的な複雑さを削減できるエンジニアリングの量はなく、できるのはその複雑さをシフトすることだけです。Appleの製品はMicrosoftの製品ほど複雑ではなく、単純にユーザー空間からエンジニアリング空間に複雑さを移します。(それは欠陥のあるアナロジーです。エネルギーを作り出すことはできませんが、エンジニアは毎ターン複雑さを作り出すという厄介な習慣を持っています。実際、多くのプログラマーは複雑さを加えて魔法使うことを楽しんでいるようです。、これはプログラマーが完全には理解していない複雑な定義です。この余分な複雑さは減らすことができます。)通常、複雑さを減らすように見えるのは、単に複雑さを他のどこか、通常はライブラリーにシフトすることです。

同様に、DIは、クライアントオブジェクトをサーバーオブジェクトにリンクする複雑さを軽減しません。Javaからそれを取り出し、XMLに移動します。これは、進行中のすべてを簡単に確認でき、コードを再コンパイルせずに物事を変更できる1つ(または少数)のXMLファイルにすべてを集中させるという点で優れています。一方、XMLファイルはJavaではないため、Javaツールで使用できないため、これは悪いことです。デバッガでデバッグしたり、静的階層構造を使用して呼び出し階層を追跡したりすることはできません。特に、DIフレームワークのバグは、検出、特定、および修正が非常に難しくなります。

したがって、DIを使用するときにプログラムが正しいことを証明するのははるかに困難です。多くの人々は、DIを使用するプログラムはテストしやすいと主張しますが、プログラムのテストではバグがないことを証明することはできません。(リンクされた論文は約40年前であり、いくつかの難解な参照があり、いくつかの古い慣行を引用していることに注意してください。はシステムにバグがない確率、pはシステムコンポーネントにバグがない確率、nはコンポーネントの数です。もちろん、この式は単純化されていますが、重要な真実を示しています。 Facebook IPO中のNASDAQクラッシュは最近の例で、コードのテストに1,000時間を費やしたが、それでもクラッシュの原因となったバグを発見できなかったと主張しました。1,000時間は半人年ですので、彼らがテストに手を出していたわけではありません。

確かに、コードが正式であることを正式に証明するタスクを実行することはほとんどありません(完了しません)。L4.verifiedなどの強力な証明の試みにより、テストでは発見されなかった大量のバグが常に発見され、テスターがバグの正確な性質をすでに知っていなければおそらく発見されないでしょう。 検査によるコードの検証を困難にするものはすべて、テスト能力が向上したとしても、バグが検出される可能性を減らすとみなすことができます

さらに、テストにはDIは必要ありません。代わりのテストバージョンではなく、システム内の実際のソフトウェアに対してシステムをテストすることを好むため、その目的で使用することはほとんどありません。テストで変更するものはすべて、プロダクションではなくテスト環境のリソースにプログラムを送るプロパティファイルに保存されます(または保存される場合もあります)。実稼働データの問題を追跡できるようになるため、モックデータベースのコーディングに時間を費やすよりも、実稼働データベースのコピーを使用してテストデータベースサーバーのセットアップに時間を費やす方がはるかに重要です(文字エンコードの問題はほんの一例です) )およびシステム統合のバグと、残りのコードのバグ。

依存関係の挿入は、依存関係の反転にも必要ありません。静的ファクトリメソッドを使用して、依存関係の反転を簡単に実装できます。それは実際、1990年代にどのように行われたかです。

実際、私は10年間DIを使用してきましたが、他のサードパーティライブラリを使用するようにサードパーティライブラリを構成できること(それがHibernateを使用するようにSpringを設定し、Log4Jを使用する)およびプロキシを介したIoCの有効化。サードパーティのライブラリを使用せず、IoCを使用していない場合は、あまり意味がなく、実際に不当な複雑さが追加されます。


1
私は同意しません、複雑さを減らすことは可能です。私はそれをやったので知っています。もちろん、いくつかの問題/要件は複雑さを強制しますが、それ以上は労力をかけて大部分を取り除くことができます。
リッキークラークソン

10
@リッキー、それは微妙な違いです。先ほど言ったように、エンジニアは複雑さを追加でき、その複雑さを取り除くことができるため、実装の複雑さを軽減できたかもしれませんが、定義により、問題に対する最適なソリューションの複雑さを軽減することはできません。ほとんどの場合、複雑さを軽減するように見えるのは、それを別の場所(通常はライブラリ)にシフトすることです。いずれにせよ、DIが複雑さを軽減しないということは、私の主要なポイントに次ぐものです。
オールドプロ

2
@Lee、コードでDIを構成することはさらに有用ではありません。DIの最も広く受け入れられている利点の1つは、コードを再コンパイルせずにサービス実装を変更できることです。コードでDIを構成すると、それを失います。
オールドプロ

7
@OldPro-コードでの構成には型安全性の利点があり、依存関係の大部分は静的であり、外部で定義することによる利点はありません。
リー

3
@Lee依存関係が静的な場合、DIによる迂回なしで、そもそも依存関係がハードコーディングされないのはなぜですか?
コンラッドルドルフ

25

あなたの同僚があなたの質問で言ったことを考えると、あなたが抱えている問題は技術的ものではなく政治的なものであると言ってすみません。留意すべき2つのマントラがあります。

  • 「それは常に人の問題です」
  • 変化は怖い」

確かに、確かな技術的理由と利点を備えた多くの反論を提起することができますが、それは会社の他の人々との悪い状況に陥ります。彼らはそれを望んでいないというスタンスをすでに持っています(なぜなら、彼らは今の状況に満足しているからです)。ですから、あなたがそれについて固執し続けるなら、同僚との関係さえ傷つけるかもしれません。私にこれが起こって、最終的に数年前に解雇されたので、これを信じてください(私は若くて素朴です); これは、あなたが自分自身になりたくない状況です。

問題は、人々が現在の働き方を変えるまでにある程度の信頼を獲得する必要があるということです。あなたが多くの信頼を持っている立場にあるか、建築家や技術リーダーの役割を持っているなど、これらの種類の事柄を決定できる場合を除き、同僚を好転させるためにできることはほとんどありません。企業文化を好転させるために、信頼を得るには説得と努力(小さな改善策を講じるなど)に多くの時間がかかります。私たちは何ヶ月または何年ものタイムスパンについて話している。

現在の企業文化がうまく機能していないことがわかった場合は、少なくとも次の就職面接で質問すべきことがもう1つあります。;-)


23

DIを使用しないでください。忘れてください。

特定の依存関係を「作成」または「検出」してオブジェクトに渡し、そのままにしておくGoF ファクトリパターンの実装を作成しますが、DIとは呼ばず、複雑な汎用フレームワークを作成しません。シンプルで関連性を保ちます。依存関係とクラスがDIへの切り替えが可能なものであることを確認してください。ただし、工場をマスターにして、範囲を非常に限定してください。

やがて、あなたはそれが成長し、1日でもDIに移行することを望むことができます。リードが自分の時間に結論に到達し、プッシュしないでください。たとえそれを達成できなかったとしても、少なくともあなたのコードには、DIの利点のいくつか、例えば単体テスト可能なコードがあります。


3
プロジェクトに沿って成長し、「DIとは呼ばない」ことで+1
ケビンマコーミック

5
同意しますが、最終的にはDIです。DIコンテナーを使用していないだけです。1つの場所に集中するのではなく、呼び出し元に負担をかけています。ただし、DIではなく「依存関係の外部化」と呼ぶ方が賢明です。
フアンメンデス

5
DIのような象牙の塔のコンセプトを実際に理解するには、伝道者が最初に解決すべき問題があることを理解するために行ったのと同じプロセスを経なければならないことがよくありました。著者はしばしばこれを忘れます。低レベルのパターン言語に固執すると、結果としてコンテナの必要性が生じる場合があります(またはそうでない場合があります)。
トムW

@JuanMendesの応答から、サードパーティのライブラリも好きではなく、すべてを単一のアセンブリ(モノリス?)にしたいので、依存関係を外部化することは彼らが特に反対していると考えています。それが本当なら、「依存関係の外部化」と呼ぶことは、彼らの有利な点からDIを呼ぶことよりもましです。
ジョナサンノイフェルド

22

オブジェクトを単体テストおよびモックするためにDIを極端にしたプロジェクトを見てきました。そのため、DI構成は、システムを実際のコードとして実行するために必要な構成を備えたプログラミング言語になりました。

テストできない適切な内部APIが不足しているため、システムは今苦しんでいますか?システムをDIモデルに切り替えることで、単体テストが改善されることを望んでいますか?コードを単体テストするためにコンテナは必要ありません。DIモデルを使用すると、モックオブジェクトを使用して簡単に単体テストを行うことができますが、必須ではありません。

一度にDI互換にするためにシステム全体を切り替える必要はありません。単体テストを勝手に書くべきではありません。機能やバグの作業チケットで遭遇したコードをリファクタリングします。次に、触れたコードが簡単にテストできることを確認します。

CodeRotを病気と考えてください。あなたは物事を勝手にハッキングし始めたくありません。あなたには患者がいて、痛みのある場所が見えるので、その場所とその周辺のものの修正を始めます。そのエリアがきれいになったら、次へ進みます。遅かれ早かれ、コードの大部分に触れることになり、この「大規模な」アーキテクチャの変更は必要なくなりました。

DIとMockのテストが相互に排他的であることは好きではありません。そして、行ったプロジェクトを見た後、DIを使用してクレイジーな、より良い言葉がないために、私は利益を見ることなく疲れます。

DIを使用する意味があるいくつかの場所があります。

  • データをストーリー化するための戦略を交換するデータアクセス層
  • 認証および承認レイヤー。
  • ユーザーインターフェイスのテーマ
  • サービス
  • 独自のシステムまたはサードパーティが使用できるプラグイン拡張ポイント。

各開発者がDI構成ファイルの場所を知る必要があります(コードを使用してコンテナーをインスタンス化できることを認識していますが、なぜそうするのでしょうか)。ああ、IRegExCompiler、DefaultRegExCompiler、SuperAwesomeRegExCompilerがあります。しかし、コード内のどこでも、Defaultが使用され、SuperAwesomeが使用されている場所を分析することはできません。

誰か私にもっと良いものを見せてくれて、彼らの言葉で私を説得しようとしたら、私自身の人格はより良く反応するでしょう。システムを改善するために時間と労力を費やすだろう誰かについて言わなければならないことがあります。多くの開発者が製品を本当に良くするのに十分な気遣いを見つけられない。システムに実際の変更を加えてそれらにアクセスし、それがどのようにあなたにとってより良く簡単になったかを示すことができれば、それはどんなオンライン調査よりも価値があります。

要約すると、システムに変更を加えるための最良の方法は、パスするたびに現在触れているコードをゆっくりと改善することです。遅かれ早かれ、システムをより良いものに変えることができます。


「DIとモックのテストは相互に排他的です」それは間違っています。相互に包括的でもありません。彼らはただ一緒にうまく機能します。また、DIが適している場所、サービスレイヤー、dao、およびユーザーインターフェイスも一覧表示します。
ニムチンプスキー

@Andrewの有効なポイント。もちろん、私はそれが行き過ぎたくありません。非常に複雑なビジネスルールを実装する必要があるため、ビジネスクラスの自動テストが最も必要でした。そして、私はまだコンテナなしでそれらをテストできることを理解していますが、個人的には、コンテナを使用するのが理にかなっています。実際に、この概念を示す小さなサンプルを作成しました。しかし、それは見られていませんでした。私の間違いは、テスト/モックがどれほど簡単かを示すテストを作成しなかったことだと思います。
メル

そんなに同意!DI構成ファイルと、プログラムフローの追跡に与える影響は本当に嫌いです。全体が「離れた場所での不気味な行動」に変わり、断片間の明らかなつながりはありません。ソースファイルよりもデバッガでコードを読みやすくなると、何かが本当に間違っています。
ザンリンクス

19

DIは、侵襲的なInversion of Controlコンテナまたはモックフレームワークを必要としません。コードを分離し、シンプルなデザインでDIを許可できます。組み込みのVisual Studio機能を使用して単体テストを実行できます。

公平を期すために、同僚にはポイントがあります。これらのライブラリを不適切に使用すると(また、それらのライブラリに慣れていないため、学習に苦労します)、問題が発生します。最後に、コードが重要です。テストのために製品を台無しにしないでください。

これらのシナリオでは妥協が必要であることを忘れないでください。


2
DIはIoCコンテナを必要としないことに同意します。ただし、コードはコンテナに依存しないように設計されているため(少なくともこの実装では)侵襲的ではありません(ほとんどの場合、コンストラクターの注入)。したがって、DIコンテナがなくてもすべて動作します。具体的な実装を依存関係を持つ他のコンストラクタに「注入」するパラメータなしのコンストラクタを配置します。そうすれば、私はまだ簡単にそれをあざけることができます。確かに、DIは実際に設計によって達成されています。
メル

+1妥協のため。誰も妥協したくない場合、誰もが苦しみます。
-Tjaart

14

これは独断的な決定であるため(または、@ Spoikeがより丁寧に言ったように政治的であるため)、これ以上DIを販売できるとは思わない。

この状況では、SOLID設計原則のような広く受け入れられているベストプラクティスと議論することはもうできません。

あなたよりも政治的な力を持っている人は、DIを使わないという(おそらく間違った)決定をしました。

反対側では、確立されたコンテナの使用が禁止されているため、独自のコンテナを実装することでこの決定に反対しています。あなたが試みたこの信仰の方針は、状況をさらに悪化させた可能性があります。

分析

私の意見では、テスト駆動開発(TDD)と単体テストのないDIには、初期の追加コストを上回る大きな利点はありません。

この問題は本当に

  • DIは必要ないのですか?

またはこれについての詳細

  • tdd / unittestingはリソースの無駄ですか?

[更新]

あなたからあなたのプロジェクトが表示されている場合は、プロジェクト管理ビューで古典的なミス、あなたが見ます

#12: Politics placed over substance.
#21: Inadequate design. 
#22: Shortchanged quality assurance.
#27: Code-like-hell programming.

他の人が見る間

#3: Uncontrolled problem employees. 
#30: Developer gold-plating. 
#32: Research-oriented development. 
#34: Overestimated savings from new tools or methods. 

現在、チームで単体テストを行っていません。あなたの最後の声明が注目されていると思います。私は数回ユニットテストを提起しましたが、誰もそれに対する本当の興味を示しませんでした、そして、我々がそれを採用することができない理由が常にありました。
メル

10

自分のチームに原則を「販売」しなければならない場合は、おそらく数マイル先に間違った道を進んでいるでしょう。

誰も余分な仕事をしたくないので、あなたがそれらを売ろうとしているものが余分な仕事のように見えるならば、あなたは彼らを優れた議論の繰り返しの呼び出しによって説得するつもりはありません。ワークロードを増やすのではなく、ワークロードを減らす方法を示していることを知っておく必要があります。

現在、 DIは複雑さを軽減する可能性を秘めた余分な複雑さとしてしばしば見られます。また、多くの場合、DIはYAGNIの単なる別のレイヤーです。そして、この複雑さのコストはゼロではありません。実際、このタイプのパターンに慣れていないチームにとってはかなり高いものです。そして、それは、利益を決してもたらさないかもしれないバケットに削り取られる本当のドルです。


適切な場所に配置する最善の策は、DIを一般的な場所ではなく、目立って便利な場所で使用することです。たとえば、多くのアプリはDIを使用してデータベースを選択および構成します。また、アプリにデータベースレイヤーが付属している場合、それを置くのは魅力的な場所です。ただし、アプリにコードがしっかりと統合されている組み込みのユーザー非表示データベースが付属している場合、実行時にDBを置き換える必要が生じる可能性はゼロに近づきます。そして、「見て、私たちは決してしたく​​ないことを簡単にできる」と言ってDIを売ると、上司と一緒に「この人には悪い考えがあります」リストに載る可能性があります。

しかし、代わりに、リリースで通常IFDEFが出力されるデバッグ出力があるとします。また、ユーザーに問題が発生するたびに、サポートチームはログ出力を取得できるように、デバッグビルドを送信する必要があります。ここでDIを使用すると、LogConsole開発者、LogNull顧客、およびLogNetwork問題のある顧客向けに、ログドライバーを切り替えることができます。1つの統合されたビルド、ランタイム構成可能。

それからDIと呼ぶ必要はありません。代わりに、単に「Mel's really good idea」と呼ばれ、悪いリストではなく良いアイデアのリストに載っています。人々は、パターンがどれだけうまく機能しているかを見て、コードの中で意味のある場所でそれを使い始めます。

アイデアを適切に販売すると、人々はあなたが何かを確信したことを知りません。


8

もちろん、Inversion of control(IoC)には多くの利点があります。コードを簡素化し、典型的なタスクを実行する魔法を追加します。

しかしながら:

  1. それは多くの依存関係を追加します。Springで書かれたシンプルなhello worldアプリケーションは、数十MBに達することがあります。Springは、コンストラクターとセッターの数行を避けるためだけに追加されました。

  2. 上記の魔法を追加します。これは部外者(たとえば、ビジネスレイヤーに何らかの変更を加える必要があるGUI開発)には理解しにくいものです。素晴らしい記事「Innovative Minds Do n't Alike Think-New York Times」を参照してください。この記事では、この効果が専門家によってどのように過小評価されているかを説明しています。

  3. クラスの使用状況を追跡することは困難です。EclipseなどのIDEには、特定のクラスまたはメソッド呼び出しの使用状況を追跡する優れた機能があります。ただし、依存関係の注入とリフレクションが呼び出されると、このトレースは失われます。

  4. IoCの使用は、通常、依存関係の注入で終わりません。無数のプロキシで終わり、数百行をカウントするスタックトレースを取得します。その90%はプロキシであり、リフレクションを介して起動します。スタックトレース分析が非常に複雑になります。

それで、私自身はSpringとIoCの大ファンであり、最近、上記の質問を再考しなければなりませんでした。私の同僚のいくつかは、PythonとPHPとによって支配ウェブ世界からJavaの世界の内部で撮影したWeb開発されている明白な javaistのもののためには、彼らのために明白より何でもあります。私の他の同僚の中には、(もちろん文書化されていない)トリックを行っており、エラーを見つけるのが非常に困難です。もちろん、IoCの誤用により、物事が軽くなります;)

私のアドバイス、あなたが仕事でIoCの使用を強制するなら、あなたは他の誰かが作るどんな誤用に対しても責任を負わされるでしょう;)


すべての強力なツールと同様に、+ 1は簡単に悪用されるため、使用しないタイミングを理解する必要があります。
フィル

4

ChrisFは何か正しいことをしていると思います。DI自体を販売しないでください。しかし、コードの単体テストについてのアイデアを売り込んでください。

DIコンテナーをアーキテクチャーに入れる1つのアプローチとして、テストでゆっくりと実装をそのアーキテクチャーに適したものにすることができます。たとえば、ASP.NET MVCアプリケーションのコントローラーで作業していると言えます。次のようにクラスを作成するとします。

public class UserController {
    public UserController() : this (new UserRepository()) {}

    public UserController(IUserRepository userRepository) {
        ...
    }

    ...
}

これにより、ユーザーリポジトリの実際の実装から切り離された単体テストを作成できます。ただし、このクラスは、DIコンテナーを使用せずに作成して、引き続き機能します。パラメータなしのコンストラクタは、デフォルトのリポジトリで作成します。

同僚がこれがはるかにクリーンなテストにつながることを見ることができれば、多分彼らはそれがより良い方法であることに気付くでしょう。たぶんあなたは彼らにそのようなコーディングを始めさせることができます。

特定のUserRepository実装を知っているUserControllerよりも優れた設計であることがわかったら、次の動きに進み、必要なインスタンスを自動的に提供できるコンポーネントであるDIコンテナーがあることを示します。


すばらしいです!DIを取得できない場合でも、自動化された単体テストが本当に必要なため、現在これを行っています。私はユニットテストはまた、私たちのチームで行われていない言及するのを忘れてしまった:(ので、私は最初のものになります。
メル

2
その場合、それがここでの本当の問題だと思います。単体テストに対する抵抗の根源を見つけて、それを攻撃してください。とりあえずDIをうそにしてみましょう。テストはより重要です。
クリスチャンホースダル

@ChristianHorsdal同意します。簡単にモック/テストできるように疎結合にしたかったです。
メル

そのアイデアは本当にクールです...テストのための素晴らしい売り
bunglestink

2

おそらく、最善の方法は、一歩下がって、DIコンテナーを導入する理由を自問することです。テスト容易性を改善する場合、最初のタスクは、チームにユニットテストを購入させることです。個人的には、DIコンテナの欠如よりも単体テストの欠如の方がずっと心配です。

1つまたは複数の単体テストを備えた包括的なスイートを使用すると、時間の経過とともにデザインを進化させることを自信を持って設定できます。それらがなければ、進化する要件に合わせて設計を維持するためのますます困難な闘争に直面します。この闘争は最終的に多くのチームが負けます。

したがって、最初にユニットテストとリファクタリングを行い、次にDI、最後にDIコンテナを導入することをお勧めします。


2

私はあなたのチームからいくつかの大きなノーを聞いています:

  • 「サードパーティライブラリなし」-別名「Not Invented Here」または「NIH」。恐れは、サードパーティライブラリを実装し、コードベースをそれに依存させることにより、ライブラリにバグ、セキュリティホール、または克服する必要のある制限がある場合でも、この3番目に依存するようになることです。コードを機能させるためにライブラリを修正するパーティ。その間、見事に失敗したか、ハッキングされた、または彼らが要求したことをしない「あなたの」プログラムのため、あなたはまだ責任を負います(そしてサードパーティの開発者は彼らのツールが「現状のままで"、 自己責任)。

    反論は、問題を解決するコードが既にサードパーティのライブラリに存在するということです。コンピューターをゼロから構築していますか?Visual Studioを作成しましたか?.NETの組み込みライブラリを避けていますか?いいえ。Intel/ AMDとMicrosoftにはある程度の信頼があり、彼らは常にバグを見つけます(そして、すぐにバグを修正するとは限りません)。サードパーティのライブラリを追加すると、車輪を再発明することなく、メンテナンス可能なコードベースをすばやく機能させることができます。そして、Microsoftの弁護士の軍隊は、.NET暗号化ライブラリのバグにより、.NETプログラムの使用中にクライアントがハッキングされた事件の責任を負うことを伝えると、あなたの顔を笑います。Microsoftは確かに、自社製品の「ゼロアワー」セキュリティホールを修正するためにジャンプしますが、

  • 「すべてを1つのアセンブリに詰め込みたい」-実際はそうではない。少なくとも.NETでは、1行のコードを変更した場合、そのコード行を含むアセンブリ全体を再構築する必要があります。適切なコード設計は、可能な限り独立して再構築できる(または、少なくとも依存関係ではなく依存関係のみを再構築する必要がある)複数のアセンブリに基づいています。本当にEXE(特定の状況で価値がある)だけであるEXEをリリースしたい場合、そのためのアプリがあります。ILMerge。

  • 「DIはタブー」-WTF?DIは、「インターフェイス」コード構造を持つO / O言語で受け入れられているベストプラクティスです。オブジェクト間の依存関係を疎結合していない場合、チームはどのようにしてGRASPまたはSOLID設計手法を遵守しますか?彼らが気にしないなら、彼らは製品を複雑な泥沼にするスパゲッティ工場を永続させています。現在、DIはオブジェクト数を増やすことで複雑さを増し、別の実装への置き換えを容易にしますが、インターフェイス自体の変更を困難にします(変更する必要のあるコードオブジェクトの追加レイヤーを追加することにより)。

  • 「すでに複雑なプロジェクトにこの複雑さの層を追加する必要はありません」-彼らはポイントを持っているかもしれません。コード開発で考慮すべき重要なことはYAGNIです。「あなたはそれを必要としません」。最初からしっかりと設計されたデザインは、「信仰に基づいて」ソリッドにされたデザインです。SOLID設計が提供する変更の容易さを必要とするかどうかはわかりませんが、気がついています。あなたは正しいかもしれませんが、非常に間違っているかもしれません。非常に堅実な設計は、最終的に「ラザニアコード」または「ラビオリコード」と見なされる可能性があり、非常に多くのレイヤーや一口サイズのチャンクがあり、非常に多くのレイヤーを掘り下げたりトレースしたりする必要があるため、一見些細な変更を加えることが難しくなりますそのような複雑な呼び出しスタック。

    私は、3つのストライクルールをサポートしています:動作させる、きれいにする、しっかりさせる。最初にコード行を記述するとき、それは機能する必要があり、象牙の塔の設計のためのポイントを取得しません。それは一度限りだと仮定し、あなたがやらなければならないことをしてください。そのコードを2度目に見ると、おそらくそれを拡張したり再利用したりすることを考えていることになります。この時点で、リファクタリングしてよりエレガントで理解しやすくします。複雑なロジックを単純化し、ループやメソッド呼び出しに繰り返しコードを抽出し、所定の場所に残しておく必要があるために、いくつかのコメントをあちこちに追加します。そのコードを3回目に見ると、おそらく大したことです。これで、SOLIDルールを順守する必要があります。依存関係を構築するのではなく、依存関係を注入し、コードの「肉」との密着度が低いメソッドを保持するクラスを抽出します。

  • 「異なる実装をプラグインするようなものではありません」-あなたは、あなたの手で単体テストを信じていないチームを持っています。私は、それらの副作用を持つオブジェクトを「モックする」ことができずに、単独で実行され、副作用のない単体テストを書くことは不可能であると仮定します。重要なプログラムには副作用があります。プログラムがDBの読み取りまたは書き込み、ファイルのオープンまたは保存、ネットワークまたは周辺機器ソケットを介した通信、別のプログラムの起動、ウィンドウの描画、コンソールへの出力、またはプロセスの「サンドボックス」外のメモリまたはリソースを使用する場合、副作用。それがこれらのことを何もしない場合、それは何をし、なぜ私はそれを買うべきですか?

    公平を期すために、単体テストはプログラムが機能することを証明する唯一の方法ではありません。統合テスト、数学的に証明されたアルゴリズムへのコードなどを書くことができます。しかし、ユニットテストは、入力A、B、Cが与えられると、このコードが期待されるXを出力することを示します。コードは、特定の場合に適切に機能します(または機能しません)。単体テストのスイートは、コードがすべての場合に期待どおりに機能することを高い信頼性で実証でき、数学的証明では不可能なことも提供します。プログラムが意図したとおりに動作することを示すデモ。アルゴリズムの数学的証明は、それが正しく実装されたことを証明せず、加えられた変更が正しく実装され、それを破らなかったことを証明しません。

要するに、このチームがDIの機能に価値を見出せない場合、彼らはそれをサポートせず、誰もが(少なくともすべてのシニアの人たちは)本当の変化のために何かを買う必要があるということです。発生する。彼らはYAGNIに多くの重点を置いています。あなたは、SOLIDおよび自動化されたテストに多くの重点を置いています。真ん中のどこかに真実がある。


1

承認されていない場合は、プロジェクトに余分な機能(DIなど)を追加するよう注意してください。時間制限のあるプロジェクト計画がある場合、承認された機能を完了するのに十分な時間がないというtrapに陥る可能性があります。

現在DIを使用していない場合でも、テストに参加して、会社でのテストに対する期待を正確に把握してください。別のプロジェクトがあり、現在のプロジェクトとDIのテストの問題を比較することができます。

DIを使用しない場合は、創造性を発揮し、コードをDI-「準備完了」にすることができます。パラメーターなしのコンストラクターを使用して、他のコンストラクターを呼び出します。

MyClassConstructor()
{
    MyClassConstructor(new Dependency)
    Property = New Dependent Property
}

MyClassConstructor(Dependency)
{
    _Dependency = Dependency
}

0

実際、彼らの最大の懸念の1つは正反対だと思います。DIはプロジェクトを複雑にしないということです。ほとんどの場合、多くの複雑さが軽減されます。

DIなしで考えるだけで、必要な依存オブジェクト/コンポーネントを取得するにはどうすればよいですか?通常は、リポジトリからのルックアップ、シングルトンの取得、またはインスタンス化によるものです。

前者2は、依存コンポーネントを見つけるための追加のコードを必要とします。DIの世界では、ルックアップを実行するために何もしませんでした。実際、コンポーネントの複雑さは軽減されます。

適切でない場合に自分自身をインスタンス化する場合、さらに複雑になります。オブジェクトを正しく作成する方法、オブジェクトを実行可能な状態に初期化する値を知る必要がありますが、これらはすべてコードに余分な複雑さをもたらします。

したがって、DIはプロジェクトを複雑にするのではなく、コンポーネントを単純にすることで、プロジェクトを単純にします。

別の大きな議論は、異なる実装をプラグインしないことです。はい、量産コードでは、実際の量産コードに多くの異なる実装があることは一般的ではありません。ただし、異なる実装を必要とする主要な場所が1つあります。それは、単体テストのMock / Stub / Test Doublesです。DIを機能させるために、ほとんどの場合、インターフェイス駆動型の開発モデルに依存し、ユニットテストでのモック/スタブ化を可能にします。実装の置き換えとは別に、「インターフェイス指向の設計」は、よりクリーンで優れた設計にもなります。もちろん、DIなしでインターフェイスを使用して設計することもできます。ただし、単体テストとDIにより、このようなプラクティスを採用する必要があります。

社内の「神」が「シンプルなコード」、「ユニットテスト」、「モジュール化」、「単一責任」などをすべて反対していると思わない限り、DIの使用を拒否する理由はないはずです。


これは理論上は素晴らしいことですが、実際には、DIコンテナを追加するとさらに複雑になります。あなたの議論の2番目の部分は、なぜそれが仕事に値するのかを示していますが、それでもまだ仕事です。
シュリンゲル

実際、私の過去の経験から、DIは複雑さを増す代わりに削減しています。はい、システムにいくつかの追加が追加されており、これが複雑さの一因となっています。ただし、DIを使用すると、システム内の他のコンポーネントの複雑さが大幅に軽減されます。最後に、実際には複雑さが軽減されます。2番目の部分については、ユニットテストを行っていない限り、余分な作業ではありません。DIがなければ、単体テストの作成と保守はほとんど困難になります。DIを使用すると、労力が大幅に削減されます。したがって、実際には仕事の削減です(ユニットテストを信頼しておらず、信頼できない場合を除く)
エイドリアンシャム

私は一つのことを強調する必要があります:私の応答のDIは、既存のDIフレームワークを意味しません。これは、アプリのコンポーネントを設計/開発する方法論にすぎません。コードがインターフェイス駆動型であり、コンポーネントがルックアップ/作成ではなく依存関係が注入されることを期待している場合、多くのメリットがあります。このような設計では、も、あなたは部品の配線(無汎用DIのFW)を行い、いくつかのアプリケーション固有の「ローダー」を書いている、あなたはまだ私が述べたものから恩恵を受けている:複雑さの軽減、およびユニットテストで仕事を減らす
エイドリアンシャム

0

「可能であれば、すべてを1つのアセンブリに詰め込むだけです。DIは、メリットのない複雑なものです。」

利点は、インターフェイスへの設計を促進し、簡単にモックできることです。その結果、ユニットテストが容易になります。単体テストは、信頼性が高く、モジュール化され、メンテナンスが容易なコードを保証します。あなたの上司がまだ悪い考えを維持している場合、あなたができることは何もありません-彼らが反対している受け入れられた業界のベストプラクティス。

DIフレームワークとモック作成ライブラリを使用してユニットテストを作成してもらい、すぐにそれを愛するようになります。


システム内のクラスの数を文字通り2倍または3倍にすることが、今では便利だと感じています。これは、メソッドごとに1つのテストを記述するか、意味のある単体テストを記述することで、100%の単体テストのカバレッジを取得しようとする議論に似ています。
アンドリューTフィンネル

わかりません、ユニットテストは良い考えだと思いますか?もしそうなら、あなたもオブジェクトをモックしますか?これは、インターフェイスとDIの方が簡単です。しかし、それはあなたが反対していることのようです。
ニムチンプスキー

それは本当に鶏と卵の問題です。インターフェイス/ DIとユニットテストは互いに密接に関連しているため、一方を他方なしで実行するのは困難です。しかし、ユニットテストは、既存のコードベースで開始する方が優れた簡単な方法であると考えています。古いkludgyコードを次第に凝集性のあるコンポーネントにリファクタリングします。その後、newこれらのコンポーネントがすべて揃っているので、オブジェクトの作成はどこでも行ってファクトリクラスを記述するのではなく、DIフレームワークを使用して行う必要があると主張できます。
スポイケ

0

これらの人々と一緒に小さなプロジェクトで仕事をしていますか、それとも大きなプロジェクトだけですか?チーム/会社のパンとバターよりも、二次/三次プロジェクトで新しいことを試すように人々を説得する方が簡単です。主な理由は、それが失敗した場合、削除/交換のコストがはるかに小さく、小さなプロジェクトのどこにでもそれを取得する作業がはるかに少ないことです。既存の大規模なものでは、どこでも一度に変更を行うための大きな初期費用が発生するか、各クラスがどのように設計されているかを覚えておく必要があるため、コードが古い/新しいアプローチの混合であり、摩擦が加わる期間が長くなります動作するように。


0

DIを促進するものの1つは単体テストですが、場合によっては複雑な層が追加されると考えています。

  • より良いテストで車を持っているが、テストで簡単に車と修理でハードよりも修理で簡単に持っていること。

  • また、DIを推進しているグループは、既存のデザインアプローチのいくつかが悪いという影響を与えることによって彼らのアイデアを提示しているだけだと思います。

  • 5つの異なる機能を実行するメソッドがある場合

    DIの人々は言う:

    • 懸念の分離はありません。[その問題は何ですか?彼らはそれを悪く見せようとしています。ロジックとプログラミングでは、競合や間違った期待を回避し、コード変更の影響を簡単に確認するために、お互いが何をしているのかを密接に知る方がはるかに優れています相互に関係のないオブジェクト100%または少なくともこれを保証することはできません{回帰テストが重要な理由がない限り}}
    • 複雑な[彼らは、さらに5つのクラスを追加してそれぞれ1つの関数を処理し、追加のクラス(コンテナ)を設定して、デフォルト(デフォルトまたはかどうかの議論、...]その後、彼らは複雑さをコンテナと構造に移しました。[私にとっては、複雑さを1つの場所から2つの場所に移すように見えます。]

DIの影響を受けるよりも、ビジネスニーズに合った独自のデザインパターンを作成する方が良いと考えています。

DIを支持して、それはいくつかの状況で役立つかもしれません。たとえば、同じインターフェースのセレクターに依存する実装が数百あり、これらの実装のロジックが大幅に異なる場合、この場合はDIまたはDI類似のテクニックを使用します。ただし、同じインターフェイスの実装がほとんどない場合、DIは必要ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.