web-dev-qa-db-ja.com

シングルトン型は静的メソッドとクラスを置き換えることができますか?

可能性のある複製:
すべての静的メソッドとシングルトンパターンの適用の違いは何ですか?

C#では、静的メソッドは長い間、クラスをインスタンス化せずにそれらを呼び出すことができるという目的を果たしてきました。後年になって、静的メソッドとクラスを使用する際の問題に気づくようになりました。

  • インターフェースを使用できない
  • 彼らは継承を使用できません
  • モックやスタブを作成できないため、テストが難しい

もっと良い方法はありますか?明らかに、インスタンス化されたクラスがなくても、常にライブラリメソッドにアクセスできる必要があります。そうしないと、コードが乱雑になります。

解決策の1つは、古い概念に新しいキーワードを使用することです:singleton。シングルトンは、通常のクラスと同じように使用できるインスタンスであるため、クラスのグローバルインスタンスです。しかし、それらを使用するには、いくつかの構文糖が必要です。

Mathクラスが実際のクラスではなくシングルトン型であるとしましょう。 Mathシングルトンのすべてのデフォルトメソッドを含む実際のクラスは、インターフェースIMathを実装するDefaultMathです。シングルトンは次のように宣言されます

singleton Math : IMath
{
   public Math
   {
      this = new DefaultMath();
   }
}

すべての数学演算を独自のクラスで置き換えたい場合は、新しいクラスを作成できます

MyMathDefaultMathを継承しますが、インターフェースIMathから継承してまったく新しいクラスを作成することもできます。クラスをアクティブなMathクラスにするには、簡単な割り当てを行います

Math =  new MyMath();

そしてボイラー!次にMath.Floorを呼び出すと、メソッドが呼び出されます。通常のシングルトンの場合、Math.Instance.Floorのようなものを記述する必要がありますが、コンパイラーはInstanceプロパティの必要性を排除していることに注意してください

別のアイデアは、シングルトンをLazyとして定義して、最初に呼び出されたときにのみインスタンス化されるようにすることです。

lazy singleton Math : IMath

あなたはどう思いますか、それは静的メソッドとクラスよりも優れた解決策でしたか?このアプローチに問題はありますか?

補遺

いくつかの点が指摘されましたが、静的メソッドの主な利点の1つは、「ステートレス」であり、並行性の観点からある程度副作用がないメソッドを持つことです。それが妥当な点だと私は心から同意します。ただし、ここでは2つの異なる問題を混在させています。1つは、インスタンスを明示的に作成する必要なしに、いくつかのメソッドを呼び出し可能かつグローバルにアクセス可能にすることです。もう1つは、ステートレスなメソッドを持つことです。

2番目の問題は、staticと同様に、ステートレスキーワードのようなメソッドがメソッドを呼び出すことを防ぐか、副次的効果を強制しないためにさらに多くのことを行うメソッドによって解決できます。静的クラスではなくシングルトンと、ステートレスクラスやメソッドのようなものを使用すると、次のような長所と短所があります。

Pro's

  • 他のクラスおよびフレームワーククラスの「静的」メソッドは、オーバーライドできるように簡単に設計できます
  • クラスの方がテストしやすい
  • 静的クラスの代わりにインスタンスを使用すると、設計パターンがうまく機能します(ファクトリーのようなもの)
  • Staticとは対照的に、継承とポリモーフィズムに制限はありません

短所

  • おそらく少し悪いパフォーマンスですか?
  • 悪いプログラマーは、依存関係注入を使用する代わりに、すべてをシングルトンでグローバルにアクセスできるようにしますが、グローバル変数を回避するために、プロパティ/フィールドではなくシングルトンメソッドにのみアクセスできるようにする必要があります:)

おそらくMathは悪い例でしたが、.Net文字列メソッドが非効率的だった場合、このメソッドを使用して簡単に独自のものに置き換えることができると想像してください。または、一部のサードパーティクラスには、わずかに変更したいシングルトンメソッドがあり、そのメソッドを継承して変更できます

16
Homde

静的メソッドをシングルトンのインスタンスメソッドに変更しても、危険性に違いはありません。それらは事実上同じです。

時々あなたは単に機能を持っています。数学的には関数は状態を含みません。これは、出力を決定するために入力に適用する計算です。たとえば、_Math.Sqrt_には状態が含まれていません。指定した値に対して計算を実行し、その値から派生した応答を返します。 C、C++、Ruby、Perl、および単純な関数をサポートするその他の言語では、その関数を静的またはその他のクラスに関連付ける必要はありません。 JavaおよびC#では、すべての関数を何らかのクラスに関連付ける必要があります。これは技術的にはハックです。

静的メソッドのままにするには、真の関数が完全に問題ないことに注意することが重要です。たとえば、Mathクラスのすべてが適切に定義されています。 「静的メソッドなし」標準を満たすためだけにMath.Instance.Sqrt(x)のような愚かなことをするのは好きではありません。

真実は、この場合のInstanceプロパティは、ほとんどの問題を引き起こす静的メソッドのタイプになるでしょう。理由?関数ではなく、状態を維持し、内部状態に基づいて動作を変更します。最初のアクセス時に、グローバルインスタンスが存在しないと判断して作成します。その後のアクセスでは、そのインスタンスを再利用します。マルチスレッド環境では、これにより競合状態が発生し、Mathクラスの複数のインスタンスが作成される可能性があります。最終的に1つは勝ち、格納されたままになりますが、その他はガベージコレクションされます。 Mathクラスを使用すると、競合状態は手頃な非効率になりますが、シングルトンがすべてのクライアントからの状態を維持する必要がある場合、それは問題になります。シングルトンアクセスの周りにlocksを追加すると、マルチスレッドアプリケーションの主要なパフォーマンスボトルネックが発生し、スレッドの数が増えるだけで悪化します。これは特に、インスタンスが作成されるまでロックが必要なためです。

関数をできる限り関数に近づけて、これらのマルチスレッドシングルトンの問題について心配する必要はありません。関数をサポートする言語では、それらを関数として実装します。静的メソッドを必要とする言語では、それらを静的メソッドにします。シングルトンは、プロジェクトを深く掘り下げたときに初めて明らかになる多くの問題をもたらします。

15
Berin Loritsch

質問が「静的クラスを置き換えるために何を使用するのか?」

シングルトンは答えではありません

依存性注入 です。

とは言っても、レガシーコードベースの静的クラスを置き換えるためにシングルトンを使用しています。この場合、DIを追加するよりもこの変更を行う方がはるかに達成可能であり、テスト中の古いコードをより速く取得できます。これを見てください スタックオーバーフローの質問 これについての詳細

更新
Q1:数学関数やファイル関数などのフレームワークレベルで依存性注入をどのように使用しますか?

フレームワークレベルで? Singletonの言語サポートを追加できるので、代わりに、Math、IOなどの名前空間のDIのフレームワークにサポートを追加します。
マシン構成は、デフォルトでこれらのフレームワークフレーバーを追加するセクションを提供します。フックは、app.configファイルで削除し、独自のフック(またはテスト用の他のフック)に追加できます。

Q2:また、シングルトンが異なるクラスに使用するクラスをスコープ指定できれば、それは事実上の依存関係の注入ではないでしょうか?

混同するように聞こえるので、singleオブジェクトを表すシングルトンの代わりに、multipleオブジェクトを表し、さまざまなオブジェクトを提供しますそれがどのように呼ばれるかのコンテキストに応じて? a)それをamultipletonにして、b)与えられたMathメソッドが何を行うかを追跡しようとするおかしな悪夢になるのではないでしょうか。どのように/どこでそれを呼び出すかについて。

6
Binary Worrier

静的クラスとのインターフェースと継承の問題もありましたが、理論的なレベルではありましたが、実用的なレベルではありませんでした。

主題を扱うSOに関する質問もいくつかあります。

最後に、私は本質的に構成されているソリューションを考案しました

  • 「実際のクラス」部分と「静的クラス」部分の2つのクラスへの分離
  • クラスに擬似静的クラスを割り当てるクラス属性
  • クラスの擬似静的クラスを取得してインスタンス化する(静的)ヘルパークラス

詳細については、こちら 私のブログのミニシリーズ を参照してください。

0
devio