web-dev-qa-db-ja.com

静的クラスとメンバーの考え方とベストプラクティス

静的メンバー、または静的クラス全体に関する考えと業界のベストプラクティスについて、私は非常に興味があります。これには欠点がありますか、それともアンチパターンに参加していますか?

これらのエンティティは、機能を提供するためにクラスのインスタンス化を要求または要求しないという意味で、「ユーティリティクラス/メンバー」と見なされます。

これに関する一般的な考えと業界のベストプラクティスは何ですか?

以下のクラスとメンバーの例を参照して、私が参照しているものを説明してください。

// non-static class with static members
//
public class Class1
{
    // ... other non-static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

// static class
//
public static class Class2
{
    // ... other static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

前もって感謝します!

11
Thomas Stringer

一般に、静態、特にあらゆる種類の静態を避けます。

どうして?

  1. 静的は並行性で問題を引き起こします。インスタンスは1つしかないため、当然、同時実行間で共有されます。これらの共有リソースは並行プログラミングの悩みの種であり、共有する必要がない必要はありません。

  2. 統計はユニットテストで問題を引き起こします。ソルト実行に値するユニットテストフレームワークは同時にテストを実行します。次に、#1に遭遇します。さらに悪いことに、すべてのセットアップ/ティアダウンの要素と、セットアップコードを "共有"しようとするハッカーがテストを複雑にします。

小さなものもたくさんあります。静力学は柔軟性に欠ける傾向があります。あなたはそれらをインターフェースすることができず、それらをオーバーライドすることができず、それらの構築のタイミングを制御することができず、ジェネリックでそれらをうまく使うことができません。それらを実際にバージョン管理することはできません。

確かに静的変数の使用法があります。定数値はうまく機能します。単一のクラスでfitしない純粋なメソッドは、ここでうまく機能します。

ただし、一般的には避けてください。

18
Telastyn

機能が「純粋」であれば問題ありません。純関数は入力パラメーターでのみ動作し、それに基づいた結果を提供します。グローバル状態や外部コンテキストには依存しません。

あなた自身のコード例を見ると:

_public class Class1
{
    public static string GetSomeString()
    {
        // do something
    }
}
_

この関数はパラメーターを取りません。したがって、おそらく純粋ではありません(この関数の純粋な実装は、定数を返すことだけです)。この例はあなたの実際の問題を表すものではないと思いますが、これはおそらく純粋な関数ではないことを指摘しています。

別の例を見てみましょう:

_public static bool IsOdd(int number) { return (number % 2) == 1; }
_

この関数が静的であることには何の問題もありません。これを拡張関数にして、クライアントコードをさらに読みやすくすることもできます。拡張関数は基本的に特別な種類の静的関数です

Telastynは、静的メンバーの潜在的な問題として並行性を正しく述べています。ただし、この関数は共有状態を使用しないため、ここでは同時実行性の問題はありません。千のスレッドが並行性の問題なしにこの関数を同時に呼び出すことができます。

.NETフレームワークでは、かなり前から拡張メソッドが存在しています。 [〜#〜] linq [〜#〜] には多くの拡張関数が含まれています(例 Enumerable.Where()Enumerable.First() =、 Enumerable.Single() など)。これらは悪いとは思わないでしょう?

ユニットテストは、コードが置き換え可能な抽象化を使用している場合にメリットがあり、ユニットテストでシステムコードをテストdoubleに置き換えることができます。静的関数はこの柔軟性を禁止しますが、これは、たとえばactualデータアクセスレイヤーをfakeデータアクセス層。

ただし、いくつかの数値が奇数か偶数かによって動作が異なるオブジェクトのテストを作成する場合、IsOdd()関数を別の実装に置き換える必要はありません。同様に、テストの目的で別のEnumerable.Where()実装を提供する必要がある場合もわかりません。

そこで、この関数のクライアントコードの可読性を調べてみましょう。

オプションa(関数を拡張メソッドとして宣言):

_public void Execute(int number) {
    if (number.IsOdd())
        // Do something
}
_

オプションb:

_public void Execute(int number) {
    var helper = new NumberHelper();
    if (helper.IsOdd(number))
        // Do something
}
_

静的(拡張)関数を使用すると、最初のコードが読みやすくなり、読みやすさが非常に重要になります。そのため、適切な場所で静的関数を使用してください。

17
Pete