web-dev-qa-db-ja.com

「デフォルト」の具象クラスを、抽象化を行う別のコンストラクターに提供するコンストラクターがあるとよい設計でしょうか。

主に抽象化に依存し、具体的な実装がクライアントまたはファクトリーによってオブジェクトに渡されるようにすることで、可能な限り依存関係を反転させたいと思います。これは、テスト容易性と拡張性に非常に役立つことがわかりました。以下に簡単な例を示します。

public class Feature {
    private final IStrategy strategy; // interface

    public Feature(IStrategy strategy) {
        this.strategy = strategy;
    }
}

ただし、通常はクライアントが気にしないため、IStrategyの具体的な実装を提供することで、このクラスのクライアントに負担をかけたくありません。また、私はこのようなことをするたびに工場を提供したくないのです。代わりに私が通常やりたいことは次のとおりです。

public class Feature {
    private final IStrategy strategy; // interface

    public Feature(IStrategy strategy) {
        this.strategy = strategy;
    }

    public Feature() {
        this(new DefaultConcreteStrategy());
    }
}

技術的には、Featureは名前で言及しているため、DefaultConcreteStrategyに直接依存しています。コンパイル時の依存関係は常に存在します。ただし、動機付けされたクライアント(通常は単体テスト)が必要に応じて別のコンクリクションを注入できるため、実行時の依存関係は事実上オプションです。

これはサウンドデザインですか?このパターンに名前はありますか?誰かがより良いまたは代替のアプローチを持っていますか?

5
Ryan Palmer

このパターンに名前はありますか?

その名前があなたのより一般的な質問に簡潔な答えを与えるので、この質問から始めましょう。これは Bastard Injection(Anti)Pattern として知られています。役に立たないことに、「貧乏人のDI」という用語も、純粋な/バニラDIを表すために一般的に使用されているにもかかわらず、それを説明するために使用されることもあります。

当然のことながら、コンセンサスは、1つのユースケースを除いて、粗野な注入パターンは適切な設計ではないということです。依存関係の逆転の原則を採用していないレガシーコードベースがある場合は、コードが徐々に改善されているため、既存の密結合コンストラクターをそのままにしながら、DIをサポートする新しいコンストラクターを追加することは理にかなっています。

これが他の場所で悪いデザインである理由の本当に良い説明については、 インジェクションアンチパターン:複数のコンストラクタ を読んでください。

ただし、通常はクライアントが気にしないため、IStrategyの具体的な実装を提供することで、このクラスのクライアントに負担をかけたくありません。

このようなクライアントに負担をかけるべきではありません。これを行うと結合が作成され、コードの一部で依然依存性注入が使用されますが、依然、依存性逆転の原則に違反しています。具象クラスの解決を担当するコード( "main"の近く)に1つだけポイントがある必要があります(つまり、コンポジションルート)。それが純粋なDIで処理されるかコンテナで処理されるかは、あまり重要ではありません。重要なのは、依存関係の解決が一元化されていることです。その時点で、デフォルトのコンストラクターは注入可能な型には必要ありません。

6
David Arno

このパターンの名前があるかどうかはわかりませんが、プロジェクトでこれを頻繁に実行し、クラスの残りの部分が適切に設計されている限り、問題はありません。 TDDを実行している場合は、依存関係を取り込むコンストラクターから開始します。これにより、依存関係注入のすべての利点が得られます。クラスの実装が完了した後に便利なコンストラクターを追加しても、これらの利点は無効になりません。

pdate:コメントの1つで、特定のクラスに具体的な依存関係があるという問題が発生しました。ここでの前提(これはOPに当てはまります)は、コンパイル時の依存関係が具象型に既に存在するため、デフォルトのコンストラクターは便宜上のものにすぎません。クラスが依存関係の注入もサポートしていることを考えると、クラスを変更することなく、完全に異なる依存関係グラフを結び付けることができます。また、具体的な依存関係をなくすのは、デフォルトのコンストラクタを削除するのと同じくらい簡単です。つまり、このパターンではメンテナンスのオーバーヘッドが発生しません。

一方、ほとんどのクライアントがデフォルトで問題がない場合、すべてのクライアントに依存関係を強制的に注入すると、DRYに違反する可能性があります。これに対する一般的な解決策は、ファクトリーを導入することです。コンビニエンスコンストラクターは、不要なファクトリークラスを導入しなくても同じ目的を果たします。

0
casablanca