Poor Man's Dependency Injectionは、レガシーアプリケーションにテスト容易性を導入する良い方法ですか?


14

過去1年で、Dependency InjectionとIOCコンテナを使用して新しいシステムを作成しました。これは私にDIについて多くを教えてくれました!

ただし、概念と適切なパターンを学んだ後でも、コードを分離し、IOCコンテナーをレガシーアプリケーションに導入することは難しいと考えています。アプリケーションは、真の実装が圧倒されるほどの大きさです。値が理解され、時間が与えられたとしても。こんなことをする時間を与えられたのは誰ですか??

もちろん、単体テストをビジネスロジックに組み込むことが目標です!
テスト防止のデータベース呼び出しと絡み合っているビジネスロジック。

この記事を読みましたが、このLos Techiesの記事で説明されているように、Poor Man's Dependency Injectionの危険性を理解しています。私はそれが本当に何も切り離さないことを理解しています。
実装には新しい依存関係が必要になるため、システム全体で多くのリファクタリングが必要になることを理解しています。どんなサイズの新しいプロジェクトでも使用することは考えません。

質問: Poor ManのDIを使用して、レガシーアプリケーションにテスト容易性を導入し、ボールローリングを開始しても大丈夫ですか?

さらに、Poor ManのDIを真のDependency Injectionへの草の根アプローチとして使用することは、原則の必要性と利点を教育する価値のある方法ですか?

インターフェイスの背後でデータベース呼び出しの依存関係と抽象化を呼び出すメソッドをリファクタリングできますか?単純に抽象化することで、そのメソッドをテスト可能にします。これは、コンストラクターのオーバーロードを介してモック実装を渡すことができるためです。

今後、支援が得られると、プロジェクトを更新してIOCコンテナを実装し、抽象化を取り入れるコンストラクタを公開することができます。



2
ちょっとだけ。I consider it a challenge to decouple code and introduce an IOC container into a legacy applicationもちろんです。技術的負債と呼ばれます。これが、大規模な改造の前に、小さくて継続的なリファクタリングが望ましい理由です。主要な設計上の欠陥を減らし、IoCへの移行はそれほど難しくありません。
ライヴ

回答:


25

NerdDinnerでのPoor Man's Injectionに対する批判は、クラスを正しくセットアップすることよりも、DIコンテナーを使用するかどうかとは関係がありません。

記事では、彼らはそれを述べています

public class SearchController : Controller {

    IDinnerRepository dinnerRepository;

    public SearchController() : this(new DinnerRepository()) { }

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

は、最初のコンストラクターがクラスを構築するための便利なフォールバックメカニズムを提供する一方で、に対する緊密な依存関係も作成するため、間違っていますDinnerRepository

もちろん、Los Techiesが示唆しているように、DIコンテナーを追加するのではなく、問題のあるコンストラクター削除するのが正しい解決策です

public class SearchController : Controller 
{
    IDinnerRepository dinnerRepository;

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

残りのクラスの依存関係が適切に反転されました。これらの依存関係を自由に挿入できます。


ご意見ありがとうございます!「問題のあるコンストラクター」とそれを回避する方法を理解しています。この依存関係は何も切り離さないことを理解していますが、単体テストは可能です。私は、できるだけ早くDIを導入し、ユニットテストを実施する初期段階にいます。ゲームの早い段階で、DI / IOCコンテナーまたは工場の複雑さを回避しようとしています。前述のように、後でDIのサポートが拡大した場合、コンテナを実装し、それらの「問題のあるコンストラクタ」を削除できます。
Airn5475

デフォルトのコンストラクターは単体テストには必要ありません。モックオブジェクトを含め、必要な依存関係をクラスなしでクラスに渡すことができます。
ロバートハーベイ

2
Airn5475 @あなたがでないように注意する上であまりにも抽象化します。これらのテストは意味のある動作をテストする必要XServiceがあります- パラメータがに渡され、XRepository返されたものを返すことをテストすることは、誰にとってもあまり有用ではなく、拡張性の面でもあまり役立ちません。ユニットテストを作成して、意味のある動作をテストし、コンポーネントが狭すぎて実際にすべてのロジックをコンポジションルートにシフトしていないことを確認します。
アントP

問題のあるコンストラクターを含めると単体テストにどのように役立つかわかりませんが、確かに反対ですか?問題のあるコンストラクターを削除して、DIが適切に実装され、オブジェクトをインスタンス化するときにモックされた依存関係を渡します。
ロブ

@Rob:ありません。これは、デフォルトのコンストラクタが有効なオブジェクトを立ち上げるための便利な方法です。
ロバートハーベイ

16

ここでは、「貧しい人のDI」が何であるかについて誤った仮定をします。

依然としてカップリングを作成する「ショートカット」コンストラクターを持つクラスを作成することは、貧乏人のDIではありません

コンテナを使用せず、すべての注入とマッピングを手動で作成することは、貧乏人のDIです。

「貧乏人のDI」という言葉は、悪いことのように聞こえます。そのため、最近では「純粋なDI」という用語が推奨されています。これは、より肯定的に聞こえ、実際にはプロセスをより正確に説明しているためです。

貧乏人/純粋なDIを使用して既存のアプリにDIを導入するだけでなく、多くの新しいアプリケーションでDIを使用する有効な方法でもあります。そして、あなたが言うように、誰もが少なくとも1つのプロジェクトで純粋なDIを使用して、IoCコンテナーの「魔法」に対する責任を処理する前に、DIの仕組みを本当に理解する必要があります。


8
「貧しい人」または「純粋な」DI、どちらでも好きな方は、IoCコンテナに関する多くの問題を軽減します。以前はすべてにIoCコンテナを使用していましたが、時間が経過するほど、それらを完全に避けることを好みます。
アントP

7
@AntP、私はあなたと一緒です:私は最近100%純粋なDIであり、積極的にコンテナを避けています。しかし、私(私たち)は、私が知る限り、そのスコアに関して少数派です。
デビッドアルノ

2
悲しいことに、IoCコンテナの「魔法の」キャンプから抜け出し、ライブラリをモックするなどして、OOP、カプセル化、手回しのダブルテスト、状態検証を受け入れていることに気付いたとき、魔法の傾向はまだ続いているようですアップ。とにかく+1。
アントP

3
@AntP&David:「Pure DI」キャンプにいるのは私だけではないと聞いてうれしいです。DIコンテナは、言語の欠陥に対処するために作成されました。その意味で価値を追加します。しかし、私の考えでは、本当の答えは言語を修正する(または他の言語に移行する)ことであり、それらの上に粗雑なものを構築することではありません。
ジミージェームズ

1

レガシーチーム/コードベースのParagidmシフトは非常に危険です。

レガシーコードの「改善」を 提案し、チームに「レガシー」プログラマーがいる場合は、「彼らはそれを間違っていた」と全員に伝え、会社の残りの時間はになります。

DIフレームワークをハンマーとして使用してすべての釘を打ち破ると、すべての場合でレガシーコードが良くなるよりも悪くなるだけです。個人的にも非常に危険です。

テストケースで使用できるように、非常に限られたケースでも、テストケースが@Ignore壊れたり、さらに悪いことに不満を言うと、せいぜいマークされたテストケースを「非標準」および「外部」コードにするだけです。管理に最も力を入れているレガシープログラマーは、「ユニットテストの無駄な時間」をすべてあなただけのせいにして「正しく」書き直されます。

DIフレームワーク、または「Pure DI」のコンセプトを基幹業務アプリに導入すると、管理者、チーム、特にリード開発者のスポンサーシップがなくても、巨大なレガシーコードベースがなくなります。チーム/会社で社会的および政治的にあなたのために。このようなことをすることは非常に危険であり、最悪の場合には政治的自殺になりかねません。

依存性注入は、問題を探すソリューションです。

定義によりコンストラクタを使用する言語は、何をしているのかを理解し、コンストラクタを適切に使用する方法を理解している場合、慣例により依存性注入を使用します。これはちょうど良い設計です。

依存性注入は、非常に狭い範囲の少量の場合にのみ有用です。

  • 大きく変化するもの、または静的にバインドされた多くの代替実装があるもの。

    • JDBCドライバーは完璧な例です。
    • プラットフォームごとに異なるHTTPクライアント。
    • プラットフォームによって異なるロギングシステム。
  • フレームワークの構成コードで定義でき、起動時に自動的に検出され、プログラムの実行中に動的にロード/リロードできる構成可能なプラグインを持つプラグインシステム。


10
DIのキラーアプリは単体テストです。あなたが指摘したように、ほとんどのソフトウェアはそれ自体で多くのDIを必要としません。しかし、テストはこのコードのクライアントでもあるため、テストしやすいように設計する必要があります。特に、これは、テストで動作を観察できるデザインにシームを配置することを意味します。これには派手なDIフレームワークは必要ありませんが、関連する依存関係を明示的かつ代替可能にする必要があります。
アモン

4
開発者が、が変わる可能性があるかを知っていれば、それは戦いの半分になります。私は、「修正済み」と思われるものの多くが変更された長い開発を続けてきました。DIを使用してあちこちで将来の校正を行うことは悪いことではありません。カーゴカルトのプログラミングのすべての時間をどこでもそれを使用します。
ロビーディー

テストを過度に複雑にすることは、それらが古くなり、将来更新される代わりに無視されたとマークされることを意味します。レガシーコードのDIは、誤った経済です。彼らがやるべきことは、この「非標準で、誰も理解していない非常に複雑なコード」をすべてコードベースに入れるための「悪者」にすることです。あなたは警告されました。

4
@JarrodRoberson、それは本当に有毒な環境です。優れた開発者の重要な兆候の1つは、過去に書いたコードを振り返って、「うーん、本当に書いたの?それはとても間違っている!」と思うことです。彼らは開発者として成長し、新しいことを学んだからです。5年前に書いたコードを見て、何も問題がないと思った人は、この5年間で何も学びませんでした。彼らが過去に間違ったことを伝えた場合、彼らは敵になり、その会社を素早く逃げます。
デビッドアルノ

1
悪者に同意するかどうかはわかりませんが、私にとって重要なポイントは、ユニットテストを行うためにDIが必要ないことですコードをテストしやすくするためにDIを実装することは、問題にパッチを当てるだけです。
アントP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.