web-dev-qa-db-ja.com

GOFシングルトンパターンの実行可能な代替案はありますか?

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

GOFシングルトンパターンの特定の代替品はありますか?

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

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

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

90

嫌いなパターン 」のアレックス・ミラーは次のように引用しています:

「シングルトンが答えのように思えるとき、私はそれがしばしば賢明であることに気づきます:

  1. シングルトンのインターフェースとデフォルト実装を作成する
  2. システムの「上部」にデフォルト実装の単一インスタンスを構築します。これは、Spring構成、コード、またはシステムに応じてさまざまな方法で定義されます。
  3. 単一のインスタンスを、それを必要とする各コンポーネントに渡します(依存性注入)
74

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

シングルトンは依存関係を非表示にします。

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

依存関係を非表示にすると、カップリングの量を追跡できなくなる傾向があります。

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

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フレームワークを使用してこれを処理することも、手動で行うこともできますが、重要なことは、グローバル状態を取り除き、依存関係とコラボレーションを明示的にすることです。

93
Rasmus Faber

私が出会った最高のソリューションは、ファクトリパターンを使用してクラスのインスタンスを構築することです。パターンを使用すると、クラスを使用するオブジェクト間で共有されるクラスのインスタンスが1つしかないことをassureできます。

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

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

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にバンドルされます。

これがお役に立てば幸いです。間違いを指摘してください。

14
seuvitor

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

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

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

8
Thomas Owens

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

8
Kai

モノステート(ロバートC.マーティンのアジャイルソフトウェア開発で説明)は、シングルトンの代替手段です。このパターンでは、クラスのデータはすべて静的ですが、getter/setterは静的ではありません。

例えば:

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
        }
    }
}

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

4
Mark M

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

これが事実である場合でも、インスタンスを表す global static field/property を使用してシングルトンを作成するアプローチを検討しますが、不適切です。静的フィールドとオブジェクトではなく、オブジェクトが提供するサービスとの間のコードに依存関係を作成するため、これは不適切です。

そのため、クラシックなシングルトンパターンの代わりに、サービスコンテナーでサービスの「いいね」パターンを使用することをお勧めします。静的フィールドを通じてシングルトンを使用する代わりに、必要なサービスの種類を要求するメソッドを介して。

*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も参照してください。シングルトンを直接コンシューマーに公開することにより、コンシューマーとオブジェクトインスタンスの間に依存関係を作成するのではなく、オブジェクトによって提供されるオブジェクトサービス。

hide シングルトンパターンの使用は、可能な限り回避することが常に可能であるとは限らないか、望ましいとは限らないので、私の意見です。

4
Pop Catalin

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

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

2
David Koelle

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

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

1
plinth

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

0
David

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

シングルトンを使用している、またはシングルトンが現在使用されており、使用を避けようとしている詳細な例を挙げていただけますか?これは、シングルトンがなくても状況をどのように処理できるかという、より洗練されたソリューションを見つけるのに役立ちます。

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

0
Mecki

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

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

ほとんどのパターンやアルゴリズムと同様に、「クールだからといって」シングルトンを使用するのは、The Wrong Thing To Doです。私はたまたまシングルトンのように見える本当に「ブラックボックス」の呼び出しを必要としていました。そして、それが問題に取り組む方法であるIMOです。パターンに注意するだけでなく、より広い範囲と、インスタンスが一意である必要があるレベルを確認してください。

0
staticsan

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

0
user18834

シングルトンをポリシングするのに最適な場所は、クラスのデザインレベルだと思います。この段階で、クラス間の相互作用を図にして、何かが絶対に、アプリケーションのどの時点でもこのクラスのインスタンスが1つだけ存在することが絶対に必要かどうかを確認できるはずです。

その場合は、シングルトンです。コーディング中に便宜上シングルトンを投入する場合は、設計を再検討し、上記のシングルトンのコーディングを停止する必要があります:)

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

0
workmad3

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

0
Manrico Corazzi