依存性注入と静的メソッド


21

文字列を受け取り、文字列を出力するメソッドを使用してクラスにアプローチする方法について、今日、別の開発者と興味深い議論をしました。

例の目的のために完全に構​​成されている次のようなものを想像してください

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

文字列入力に基づいたロジックを持つ関数は、DIを使用してプロジェクトに追加され、DIコンテナが配置されています。この新しいクラスをインターフェイスで追加し、必要な場所に挿入しますか、それとも静的クラスにしますか?それぞれの長所と短所は何ですか?どこで必要なときにアクセスするだけでなく、コンストラクター注入で使用するようにしたい(またはしたくない)のはなぜですか。


1
@Ewan抽象化に役に立たないヘルパーメソッド用。例:Apache FileUtils。
ウォルフラット

3
@Ewan:副作用のない静的メソッドは、理解しやすくテストしやすいため、最良の種類のメソッドです。
ジャックB

1
しかし、彼らはそれらに依存ユニットテストのものにそれが不可能に
ユアン・

3
@Ewan Eh、そうでもない。Math.Max()は静的であるため悪いですか?静的メソッドをテストして動作している場合、他のメソッドで問題なく安全に使用できます。静的なものが失敗している場合、そのテストはそれをキャッチします。
T.サール-復活モニカ

1
「静的」が副作用を保証しない場合、私は議論を見ることができるかもしれません。しかし、そうではありません。
ユアン

回答:


25

これを注入する必要がある理由はありません。これは単なる関数であり、依存関係はないので、呼び出してください。純粋に見えるようにしたい場合は、静的にすることもできます。これに対して単体テストを簡単に書くことができます。他のクラスで使用されている場合でも、単体テストを作成できます。

依存関係のない関数を抽象化する必要はありません。やり過ぎです。

これがより複雑になった場合、コンストラクターまたはメソッドにインターフェースを渡すことが保証されます。しかし、GetStringPart場所などに基づく複雑なロジックがない限り、その道をたどりません。


2
これは正解です!悪いことに、カーゴカルトの答えは受け入れられました。
ジャックB

3
関数に依存関係がないことは重要ではありません。問題は、他のものが機能に依存し、それに密接に結合するときです
ユアン

2
関数が6か月で変化し、それほど純粋ではないが、多くの場所で既に使用されている場合はどうなりますか。それは静的なものとして拡張可能ではなく、クラスの単体テストを消費することから分離できるものでもありません。変更の機会が少しでもあれば、それを注入しに行きます。そうは言っても、私は対立する議論に耳を傾けることができます。それがこの投稿のポイントです。これが注入されることのマイナス面と静電気のプラスは何ですか?
ジェームズ

1
一般的に、このWebサイトで依存性注入を議論している人々は、依存性注入の必要はありません。したがって、不必要な複雑さへのバイアス。
フランクヒルマン

2
@James関数の純度が低下すると、何か間違ったことをしていることになり、最初から関数が適切に定義されていない可能性があります。正方形の面積の計算に突然データベース呼び出しが必要になったり、UIの視覚的な更新が展開されたりすることはありません。ソートの「サブストリング」メソッドが突然不純になるシナリオを想像できますか?
T.サール-復活モニカ

12

理由はここにあります

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

 

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

静的メソッドを使用した場合GetStringPart、古い動作を破壊するか、条件付きロジックで汚染することなく、動作を変更する方法はありません。スタティックは変装した邪悪なグローバルであることは事実ですが、それらがポリモーフィズムを無効にするという事実は、それらに関する私の主な不満です。静的メソッドは、OOP言語の最初のクラスではありません。メソッドに、状態のないオブジェクトであっても、住むオブジェクトを与えることで、メソッドを移植可能にします。その振る舞いは、変数の値のように渡すことができます。

ここでは、ヨーロッパで展開するときと米国で展開するときで少し異なる動作をする必要があるシステムを想像しました。どちらかのシステムに他のシステムにのみ必要なコードを含めるように強制するのではなく、解析オブジェクトがクライアントに挿入される順序を制御することで動作を変更できます。これにより、地域の詳細の広がりを含めることができます。また、既存のパーサーに触れることなく、OrderParserCanadaを簡単に追加できます。

それがあなたにとって何も意味しない場合、本当にこれについて良い議論はありません。

ところで、GetStringPartひどい名前です。


*大括弧のコードスタイルに集中*あなたがC#コードを記述しようとしているJavaプログラマーだと思いますか?私が推測するものを知るために1つを取る。:)
ニール

質問は言語に固有のものではなく、デザインに関するものです。静的メソッドの私の問題は、テスト用のコードの分離を妨げることです。私が話していた開発者は、私はDIに過度に依存していると考え、拡張メソッド/静的メソッドが関連している場合があります。まれなケースではあるが、一般的な目的ではないと思います。私が感じた継続的な議論は、さらに議論するのに良いものだと感じました。ポリモーフィズムのあなたの主張にも同意します。
ジェームズ

テストが理由ですが、他にもあります。これは、人々がテストを批判し始めただけなので、テストでこれを非難するのは好きではありません。簡単な事実は、手続き型のプログラミングをしないと手続き型のプログラマーが気に入らないということです。
candied_orange

単純に言えstatic getStringPartEU()ませんか?あなたの例は、そのクラスに他のメソッドがあり、それも特殊なEUの扱いを必要とし、それらを単一のユニットとして扱う必要がある場合にのみ意味があります。
ロバートハーヴェイ

2
あなたは間違いなく不必要な複雑さを支持して主張しています。すべてに、より多くのコードを追加できます。変更が必要なものは簡単に変更できるはずですが、将来変更すべきものを予測しようとしないでください。静的フィールドはグローバル変数と同一でもかまいませんが、静的メソッドは純粋なメソッドであり、可能な限り最適なメソッドです。
フランクヒルマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.