94

それに直面しよう。シングルトン パターンは、大群のプログラマーがフェンスの両側にいる非常に物議を醸すトピックです。Singleton は単に栄光に満ちたグローバル変数にすぎないと感じる人もいれば、パターンで誓ってそれを絶え間なく使用する人もいます。ただし、シングルトン論争が私の質問の中心にあることを 望んでいません。誰もが綱引きをして戦い、誰が勝つかを見ることができます。私が言おうとしているのは、単一の正解があるとは信じていないし、意図的に党派の論争を煽ろうとしているわけでもないということです。質問をするとき、私は単にシングルトン代替に興味があります:

GOF シングルトン パターンに代わる具体的な方法はありますか?

たとえば、過去にシングルトン パターンを使用したことが何度もありましたが、単純に 1 つまたは複数の変数の状態/値を保持することに関心がありました。ただし、変数の状態/値は、シングルトン パターンを使用する代わりに静的変数を使用して、クラスのインスタンス化ごとに保持できます。

他にどんなアイデアがありますか?

編集: これを「シングルトンを正しく使用する方法」についての別の投稿にしたくありません。繰り返しますが、私はそれを回避する方法を探しています。楽しみのために、いいですか?映画の予告編の最高の声で純粋に学術的な質問をしていると思います。「シングルトンのないパラレルユニバースでは、何ができるでしょうか?」

4

16 に答える 16

97

シングルトンを回避する適切な方法を理解するには、シングルトン(および一般的なグローバル状態)の何が問題になっているのかを理解する必要があります。

シングルトンは依存関係を隠します。

なぜそれが重要なのですか?

依存関係を非表示にすると、結合の量を追跡できなくなる傾向があるためです

あなたはそれを主張するかもしれません

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

よりも簡単です

void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, 
                    String creditCardNumber, int price){
  creditCardProcessor.debit(creditCardNumber, amount);
  cart.addLaptop();
}

しかし、少なくとも2番目のAPIは、メソッドのコラボレーターが何であるかを正確に明確にします。

したがって、シングルトンを回避する方法は、静的変数やサービスロケーターを使用するのではなく、シングルトンクラスをインスタンスに変更することです。インスタンスは、意味のあるスコープでインスタンス化され、それらを必要とするコンポーネントとメソッドに注入されます。IoCフレームワークを使用してこれを処理することも、手動で行うこともできますが、重要なことは、グローバル状態を取り除き、依存関係とコラボレーションを明示的にすることです。

于 2008-10-02T14:04:04.720 に答える
77

" Patterns I Hate " のAlex Millerは次のように引用しています。

「シングルトンが答えのように思えるとき、私は多くの場合、以下のほうが賢明であることがわかります:

  1. シングルトンのインターフェイスとデフォルトの実装を作成する
  2. システムの「トップ」にデフォルト実装の単一インスタンスを構築します。これは、Spring 構成、コード、またはシステムに応じてさまざまな方法で定義されている可能性があります。
  3. 単一のインスタンスを、それを必要とする各コンポーネントに渡します (依存性注入)
于 2008-10-02T12:50:04.180 に答える
14

私が見つけた最高の解決策は、ファクトリ パターンを使用してクラスのインスタンスを構築することです。このパターンを使用すると、クラスを使用するオブジェクト間で共有されるクラスのインスタンスが 1 つだけであることを保証できます。

管理が複雑になると思いましたが、このブログ投稿「Where Have All the Singletons Gone?」を読んだ後、、とても自然に思えます。余談ですが、単体テストを分離するのに大いに役立ちます。

要約すると、あなたは何をする必要がありますか?オブジェクトが別のオブジェクトに依存するときはいつでも、コンストラクターを介してのみそのインスタンスを受け取ります (クラスに new キーワードはありません)。

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

そして、工場。

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

ファクトリを 1 回だけインスタンス化するため、exSingleton のインスタンス化は 1 つだけです。buildNeedy を呼び出すたびに、NeedyClass の新しいインスタンスが exSingleton にバンドルされます。

これが役立つことを願っています。間違いがあればご指摘ください。

于 2008-10-02T13:38:16.233 に答える
9

パターンを避けるためにわざわざ行く必要はありません。パターンの使用は、設計上の決定または自然な適合のいずれかです (それは適切な位置に収まるだけです)。システムを設計するときは、パターンを使用するか使用しないかを選択できます。ただし、最終的には設計上の選択となるものを避けるためにわざわざ行くべきではありません。

私はシングルトン パターンを避けません。適切で使用するか、不適切で使用しないかのどちらかです。それはそれと同じくらい簡単だと思います。

Singleton の妥当性 (またはその欠如) は、状況によって異なります。これは、下すべき設計上の決定であり、その決定の結果を理解 (および文書化) する必要があります。

于 2008-10-02T12:40:45.547 に答える
9

Spring やその他の IoC コンテナーは、その点でかなり適切に機能します。クラスはアプリ自体の外部で作成および管理されるため、コンテナーは単純なクラスのシングルトンを作成し、必要な場所に挿入できます。

于 2008-10-02T12:43:08.473 に答える
6

モノステート(Robert C. Martinのアジャイルソフトウェア開発で説明)は、シングルトンの代替手段です。このパターンでは、クラスのデータはすべて静的ですが、ゲッター/セッターは非静的です。

例えば:

public class MonoStateExample
{
    private static int x;

    public int getX()
    {
        return x;
    }

    public void setX(int xVal)
    {
        x = xVal;
    }
}

public class MonoDriver
{
    public static void main(String args[])
    {
        MonoStateExample m1 = new MonoStateExample();
        m1.setX(10);

        MonoStateExample m2 = new MonoStateExample();
        if(m1.getX() == m2.getX())
        {
            //singleton behavior
        }
    }
}

モノステートはシングルトンと同様の動作をしますが、プログラマーがシングルトンが使用されているという事実を必ずしも認識していない方法で動作します。

于 2010-03-17T17:46:10.633 に答える
4

シングルトンパターンが存在するのは、一連のサービスを提供するために単一のオブジェクトが必要な場合があるためです

その場合でも、インスタンスを表すグローバルな静的フィールド/プロパティを使用してシングルトンを作成するアプローチは不適切だと考えています。静的フィールドとオブジェクトではなく、オブジェクトが提供するサービスとの間のコードに依存関係が作成されるため、不適切です。

そのため、古典的なシングルトン パターンの代わりに、サービス対象のコンテナーでサービスのようなパターンを使用することをお勧めします。この場合、静的フィールドを介してシングルトンを使用する代わりに、必要なサービスの種類を要求するメソッドを介してシングルトンへの参照を取得します。

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

単一のグローバルの代わりに

*pseudocode* singletonType.Instance

このようにして、オブジェクトのタイプをシングルトンから別のものに変更したい場合、簡単にそれを行うことができます。また、追加の利点として、オブジェクト インスタンスの割り当てをすべてのメソッドに渡す必要はありません。

Inversion of Controlも参照してください。シングルトンをコンシューマーに直接公開することで、オブジェクトによって提供されるオブジェクト サービスではなく、コンシューマーとオブジェクト インスタンスの間に依存関係を作成するという考え方です。

私の意見では、常にシングルトン パターンの使用を回避できるとは限らないか、または望ましいとは限らないため、可能な限りシングルトン パターンの使用を非表示にします。

于 2008-10-02T13:10:58.187 に答える
2

シングルトンを使用して単一のデータ オブジェクトを表す場合は、代わりにデータ オブジェクトをメソッド パラメーターとして渡すことができます。

(ただし、これはそもそもシングルトンを使用する間違った方法であると主張します)

于 2008-10-02T12:44:33.650 に答える
1

状態を維持したいという問題がある場合は、MumbleManager クラスが必要です。システムで作業を開始する前に、クライアントは MumbleManager を作成します。Mumble はシステムの名前です。それによって状態が保持されます。MumbleManager には、状態を保持するプロパティ バッグが含まれている可能性があります。

このタイプのスタイルは非常に C に似ており、オブジェクトにはあまり似ていません。システムを定義するオブジェクトはすべて、同じ MumbleManager への参照を持っていることがわかります。

于 2008-10-02T12:44:49.780 に答える
0

シングルトンを監視するのに最適な場所は、クラスの設計レベルだと思います。この段階で、クラス間の相互作用をマッピングして、アプリケーションの存続期間中、このクラスのインスタンスが 1 つだけ存在することが絶対に、確実に必要かどうかを確認できるはずです。

その場合は、シングルトンがあります。コーディング中に便利なようにシングルトンを投入している場合は、実際に設計を再検討し、前述のシングルトンのコーディングを停止する必要があります:)

はい、「回避」ではなく、「警察」という言葉がここで意味していました。シングルトンは避けるべきものではありません (goto 変数とグローバル変数が避けるべきものではないのと同じように)。代わりに、その使用を監視し、それが目的を効果的に達成するための最良の方法であることを確認する必要があります。

于 2008-10-02T13:05:48.543 に答える
0

シングルトンは主に「メソッドコンテナ」として使用し、状態はまったくありません。これらのメソッドを多くのクラスと共有する必要があり、インスタンス化と初期化の負担を避けたい場合は、コンテキスト/セッションを作成し、そこですべてのクラスを初期化します。セッションを参照するすべてのものは、そこに含まれる「シングルトン」にもアクセスできます。

于 2008-10-02T13:06:09.993 に答える
0

実際、Singelton を回避するためにゼロから設計する場合、静的変数を使用して Singleton を使用しないように回避する必要はありません。静的変数を使用すると、多かれ少なかれシングルトンも作成されます。唯一の違いは、異なるオブジェクト インスタンスを作成することですが、内部的にはすべてシングルトンを使用しているかのように動作します。

シングルトンを使用している場所、またはシングルトンが現在使用されていて、使用を避けようとしている場所の詳細な例を教えてください。これは、シングルトンをまったく使用せずに状況を処理する方法をより洗練された解決策を見つけるのに役立ちます。

ところで、私は個人的にシングルトンに問題はなく、シングルトンに関して他の人が抱えている問題を理解できません。私は彼らについて悪いことは何も見ていません。つまり、それらを悪用していない場合です。すべての有用な手法は悪用される可能性があり、悪用されると、否定的な結果につながります。よく誤用されるもう 1 つの手法は、継承です。それでも、一部の人々がそれをひどく乱用しているという理由だけで、継承が何か悪いことだと言う人は誰もいません。

于 2008-10-02T12:45:23.320 に答える
0

個人的には、シングルトンのように動作するものを実装するより賢明な方法は、完全に静的なクラス (静的メンバー、静的メソッド、静的プロパティ) を使用することです。ほとんどの場合、この方法で実装します (ユーザーの観点からの動作の違いは考えられません)。

于 2008-10-02T12:55:47.630 に答える
0

プレーン オブジェクトとファクトリ オブジェクトを使用します。ファクトリは、構成情報 (たとえば含まれているもの) と動作のみを使用して、インスタンスとプレーン オブジェクトの詳細をポリシングする責任があります。

于 2008-10-02T12:43:04.090 に答える
0

オブジェクト指向の強い環境 (Java など) でプログラミングを行ったことがないので、この議論の複雑さについて完全には把握できていません。しかし、私は PHP 4 にシングルトンを実装しました。これは、不完全で多少壊れたフレームワークで、自動的に初期化され、関数呼び出しを上下に渡す必要がない「ブラックボックス」データベース ハンドラーを作成する方法として実装しました。

シングルトン パターンのリンクをいくつか読んだことがありますが、まったく同じ方法でそれを再び実装するかどうかは完全にはわかりません。本当に必要だったのは、共有ストレージ (実際のデータベース ハンドルなど) を持つ複数のオブジェクトでした。

ほとんどのパターンやアルゴリズムと同様に、「クールだから」という理由だけでシングルトンを使用することは、すべきでないことです。たまたまシングルトンによく似た、真に「ブラックボックス」な呼び出しが必要でした。そして、IMO はこの問題に取り組む方法です。パターンを認識するだけでなく、より広い範囲と、そのインスタンスが一意である必要があるレベルを確認します。

于 2008-11-11T22:50:46.430 に答える
-1

どういう意味ですか、それを回避するための私のテクニックは何ですか?

それを「回避」するには、シングルトン パターンが自然に適している状況に遭遇することが多く、これらの状況を打破するために何らかの対策を講じる必要があることを意味します。

しかし、ありません。シングルトン パターンを避ける必要はありません。それは単に発生しません。

于 2008-10-02T12:57:07.037 に答える