シングルトン クラスの誤用の最も一般的な例
-
02-07-2019 - |
質問
シングルトン クラスを使いたくなるかもしれないが、シングルトン クラスを使用すべきではないのはどのような場合ですか?回避に注意すべき「単発性炎症」の最も一般的な例のリストがあれば、非常に素晴らしいと思います。
解決
増殖可能なリソースに発展する可能性のあるものにはシングルトンを使用しないでください。
おそらくばかげているように聞こえるかもしれませんが、何かをシングルトンとして宣言すると、それが絶対に一意であるという非常に強力な宣言をしていることになります。それを中心にコードを構築することが増えています。そして、何千行ものコードを書いた後で、それがまったくシングルトンではないことがわかったとき、他のすべてのオブジェクトはクラス WizBang の「」神聖なオブジェクトがシングルトンであることを期待しているため、目の前に膨大な量の作業が待っています。 。
典型的な例:「このアプリケーションが持っているデータベース接続は1つしかないため、シングルトンです。」 - 悪いアイデア。将来的には、複数の接続が必要になる場合があります。データベース接続のプールを作成し、そこに 1 つのインスタンスだけを追加することをお勧めします。シングルトンのように動作しますが、他のすべてのコードにはプールにアクセスするための拡張可能なコードが含まれます。
編集: 理論的にはシングルトンを複数のオブジェクトに拡張できることは理解しています。しかし、実際のライフサイクル (プール/アンプールなど) は存在せず、つまり、配布されたオブジェクトの実際の所有権が存在しないことを意味します。現在のマルチシングルトンは、さまざまなメソッドやスレッドで同時に使用するにはステートレスでなければなりません。
他のヒント
まあ、ほとんどの場合、シングルトンはとにかく物事を静的にしているだけです。つまり、事実上データをグローバルにしていて、グローバル変数がダメであることは誰もが知っているか、静的メソッドを書いていて、それはあまり OO ではないということですよね?
ここ シングルトンがなぜ悪いのかについての、Steve Yegge によるより詳細な暴言です。基本的に、ほとんどすべての場合にシングルトンを使用すべきではありません。複数の場所でシングルトンが必要になることが絶対にないということは実際にはわかりません。
「複数ある場合」などと答えた人も多いと思います。
元の投稿者は、(主な理由ではなく) シングルトンを使用すべきではないケースのリストを望んでいたため、私もそれに同意します。
グローバルの使用が許可されていないためにそれを使用するときはいつでも!
私がコードレビューでグローバルを受け入れないことを知っていたため、シングルトンを使用したジュニアエンジニアが私にいた回数。彼らはグローバルをシングルトン パターンに置き換えただけなのに、まだグローバルが残っているだけだと私が指摘すると、彼らはよくショックを受けたようです。
私は数年前に大きな罪を犯しました(ありがたいことに、それ以来教訓を学びました)。
何が起こったのかというと、私は VB6 から .Net に変換したデスクトップ アプリ プロジェクトに参加しましたが、本当に混乱していました。40 ページの (印刷された) 関数のようなもので、実際のクラス構造はありません。データベースへのアクセスをカプセル化するクラスを構築しました。(まだ) 実際のデータ層ではなく、実際のデータ層が使用できる単なる基本クラスです。どこかで、このクラスをシングルトンにするという素晴らしいアイデアを思いつきました。1 年ほどは問題なく動作していましたが、その後、アプリ用の Web インターフェイスも構築する必要がありました。すべての Web ユーザーが同じ接続を共有する必要があったため、シングルトンは最終的にデータベースにとって大きなボトルネックになりました。また...学んだ教訓。
振り返ってみると、他の開発者にそれを使用することについてより規律を持たせるよう強制し、以前は VB6 の世界では問題ではなかったスコープの問題を認識させることができたので、これはおそらく一時的には正しい選択だったのでしょう。しかし、数週間後に周囲に多くのものが蓄積される前に、元に戻すべきでした。
ここに暴言があります 私の友人のアレックス・ミラーより...「シングルトンを使用すべきではない場合」を正確に列挙しているわけではありませんが、包括的で優れた投稿であり、シングルトンを使用するのは、たとえあったとしてもまれな場合にのみであるべきだと主張しています。
シングルトンは、まともなパターンを非常に限定的に単純化したものにすぎないため、事実上常に悪いアイデアであり、一般に役に立たず、冗長です。
依存関係の注入がどのように機能するかを調べてください。これは同じ問題を解決しますが、より便利な方法で解決します。実際、設計のより多くの部分に適用できることがわかります。
DI ライブラリは世の中にありますが、基本的なライブラリを自分で作成することもできます。それは非常に簡単です。
シングルトンは 1 つだけ、つまりコントロール/サービス ロケーター オブジェクトの反転を持たせるようにしています。
IService service = IoC.GetImplementationOf<IService>();
それが悪夢になる傾向があるものの 1 つは、次のものが含まれている場合です。 変更可能 世界的な状態。私が取り組んだプロジェクトでは、まったく別の方法で解決すべき事柄 (戦略のパスインなど) のために至る所でシングルトンが使用されていました。「非シングルトン化」は、場合によっては、システム。シングルトンを使用する場合のほとんどの場合、それは単に間違っている、つまり最初は良さそうに見えても、特にテスト時に問題になると私は主張します。
同じ JVM で複数のアプリケーションを実行している場合。
シングルトンは、単一のアプリケーションだけではなく、JVM 全体にわたるシングルトンです。複数のスレッドまたはアプリケーションが新しいシングルトン オブジェクトを作成しているように見えても、同じ JVM で実行されていれば、それらはすべて同じものを使用していることになります。
場合によっては、どれか 1 つしか存在しないと思っていて、それが間違っていたことが判明することがあります。
例: データベース クラス。アプリのデータベースにのみ接続すると想定しています。
// Its our database! We'll never need another
class Database
{
};
ちょっと待って!あなたの上司は、他の人のデータベースに接続するように言います。Web サイトに phpbb を追加し、そのデータベースにアクセスしてその機能の一部を統合したいとします。新しいシングルトンを作成するか、データベースの別のインスタンスを作成する必要がありますか?ほとんどの人は、同じクラスの新しいインスタンスが優先され、コードの重複がないことに同意します。
むしろそうしたいです
Database ourDb;
Database otherDb;
データベースをコピー&ペーストして以下を作成するよりも、
// Copy-pasted from our home-grown database.
class OtherGuysDatabase
{
};
ここでの滑りやすい坂は、クラスの新しいインスタンスを作成することを考えるのをやめ、代わりにインスタンスごとに 1 つの型を持てば問題ないと考え始める可能性があることです。
(たとえば) 接続の場合、接続自体をシングルトンにしたくない、4 つの接続が必要になる、または接続を何度も破棄して再作成する必要があるのは当然です。
でも、なぜそうしないのですか アクセス すべての接続は 1 つのインターフェイスを介して行われます (つまり、接続マネージャー)?