web-dev-qa-db-ja.com

1つの実装を構築する多数のグループ。絶望的なDI?サービスロケーターを使用しますか?

インジェクションを受け入れるのではなく、依存関係を直接構築する1001のクライアントがあるとします。上司によると、1001のリファクタリングはオプションではありません。実際には、ソースへのアクセスも許可されていません。クラスファイルだけです。

私たちがすべきことは、これらの1001クライアントが通過するシステムを「近代化」することです。私たちは好きなものをすべてリファクタリングできます。依存関係はそのシステムの一部です。そして、新しい実装に変更することになっている依存関係の一部。

私たちがしたいのは、この多数のクライアントを満たすために、依存関係のさまざまな実装を構成する機能です。残念ながら、クライアントはコンストラクターやセッターによるインジェクションを受け入れないため、DIは選択肢のようには見えません。

オプション:

1)クライアントが使用するサービスの実装をリファクタリングして、クライアントが今必要としていることを実行します。完了しました。柔軟性がありません。複雑ではありません。

2)実装をリファクタリングして、ファクトリを通じて取得するさらに別の依存関係に作業を委任します。これで、ファクトリをリファクタリングすることで、すべての実装を制御できます。

3)実装をリファクタリングして、サービスロケーターを通じて取得するさらに別の依存関係に作業を委任します。これで、サービスロケータを構成することにより、すべての実装がどの実装を使用するかを制御できます。サービスロケータは、文字列のhashmapをオブジェクトにキャストして、少しキャストするだけです。

4)まだ考えたこともない。

目的:

無意味な複雑さを追加せずに、設計が不十分な古いクライアントコードを将来にドラッグすることによって引き起こされる設計の損傷を最小限に抑えます。

クライアントは依存関係の実装を認識または制御するべきではありませんが、newを使用してそれらを構築することを主張します。 newを制御することはできませんが、それらが構築しているクラスは制御します。

私の質問:

何を考慮しなかったのですか?


Doc Brownからの質問

異なる実装間で構成する可能性が本当に必要ですか?何のために?

機敏。多くの未知数。経営陣は変化の可能性を求めています。外の世界への依存を失うだけです。また、テスト。

ランタイムメカニクスが必要ですか、それとも異なる実装間で切り替えるためのコンパイルタイムメカニクスだけが必要ですか?どうして?

コンパイル時間のメカニズムで十分でしょう。テストを除いて。

実装を切り替えるにはどの粒度が必要ですか?一斉に?モジュールごと(それぞれがクラスのグループを含む)?クラスごと?

1001のうち、一度に1つだけがシステムを通過します。すべてのクライアントが一度に使用するものを変更しても問題ないでしょう。ただし、依存関係を個別に制御することはおそらく重要です。

誰がスイッチを制御する必要がありますか?あなた/あなたの開発者チームだけですか?管理者?各クライアントは自分で?または、クライアントのコードのメンテナンス開発者ですか?それで、力学はどれほど簡単/堅牢/誰でもできる必要がありますか?

テスト用の開発。外部ハードウェアの依存関係が変更されたときの管理者。テストと構成が簡単である必要があります。


私たちの目標は、システムをすばやく作り直し、近代化できることを示すことです。


実装スイッチの実際の使用例?

1つは、ハードウェアソリューションの準備ができるまで、一部のデータがソフトウェアによって提供されることです。

14
candied_orange

まあ、私はサポートされているソリューションの技術的な詳細とその正確な違いを完全に理解しているとは思いませんが、私が最初に本当に必要な柔軟性の種類を見つける必要があります。

自問しなければならない質問は次のとおりです。

  • 異なる実装間で構成する可能性が本当に必要ですか?何のために?

  • ランタイムメカニクスが必要ですか、それとも異なる実装間で切り替えるためのコンパイルメカニクスだけが必要ですか?どうして?

  • 実装を切り替えるにはどの粒度が必要ですか?一斉に?モジュールごと(それぞれがクラスのグループを含む)?クラスごと?

  • 誰がスイッチを制御する必要がありますか?あなた/あなたの開発者チームだけですか?管理者?各クライアントは自分で?または、クライアントのコードのメンテナンス開発者ですか?それで、力学はどれほど簡単/堅牢/誰でもできる必要がありますか?

これらの質問に対する回答を念頭に置いて、必要な柔軟性を提供する、考えられる最も単純なソリューションを選択してください。 「念のため」に確信が持てない柔軟性を実装しないでください。

回答への回答として:少なくとも1つのrealユースケースがある場合は、それを使用して設計の決定を検証します。そのユースケースを使用して、どの種類のソリューションが最適かを調べます。 「ファクトリー」または「サービスロケーター」があなたにあなたが必要とするものをあなたに提供するか、またはあなたが何か他のものを必要とするかどうか試してください。両方の解決策があなたのケースに等しく良いと思うなら、サイコロを投げます。

7
Doc Brown

これが正しいことを確認しているだけです。 _C1,...,Cn_のようないくつかのクラスで作られたサービスと、new Ci(...)を直接呼び出すクライアントの束があります。だから私はあなたのソリューションの一般的な構造に同意すると思います。それは、ニースでモダンないくつかの新しいクラス_D1,...,Dn_で新しい内部サービスを作成し、それらの依存関係を注入し(スタックを通じてそのような依存関係を許可)、Ciを書き換えることですDiをインスタンス化してデリゲートする以外に何もしません。問題はその方法であり、_2_と_3_でいくつかの方法を提案しました。

_2_に同様の提案をする。 Di全体および内部で依存性注入を使用し、オブジェクトグラフの組み立てを担当する通常のコンポジションルートRを作成できます(適切と思われる場合はフレームワークを使用)。静的ファクトリの背後にRを突き詰め、各CiDiを取得させます。したがって、たとえば、

_public class OldClassI {
    private final NewClassI newImplementation;

    public OldClassI(Object parameter) {
        this.newImplementation = CompositionRoot.getInstance().getNewClassI(parameter);
    }
}
_

基本的にこれはソリューション2ですが、依存関係の注入の他の部分とともに、すべてのファクトリを1つの場所に収集します。

2
walpen

シンプルで派手ではない、特異な実装から始めます。

後で追加の実装を作成する必要がある場合は、実装の決定がいつ行われるかが問題になります。

「インストール時」の柔軟性が必要な場合(各クライアントのインストールで単一の静的な実装が使用されます)、特別なことをする必要はありません。異なるDLLまたはSO(またはlibフォーマットが何であれ)を提供するだけです。クライアントは、正しいものをlibフォルダーに配置するだけです...

実行時の柔軟性が必要な場合は、シンアダプターと実装選択メカニズムだけが必要です。ファクトリー、ロケーター、IoCコンテナーのいずれを使用するかにかかわらず、ほとんどの場合問題があります。アダプタとロケータの唯一のsignificantの違いは、A)ネーミングとB)返されたオブジェクトがシングルトンか専用インスタンスかどうかです。 IoCコンテナーとファクトリー/ロケーターの大きな違いはwhoはwhoを呼び出すです。 (多くの場合、個人的な好みの問題です。)

1
svidgen