it-swarm-eu.dev

Singleton: Wie soll es verwendet werden

Bearbeiten: Von einer anderen Frage habe ich eine Antwort bereitgestellt, die Links zu vielen Fragen/Antworten zu Singletons enthält: Weitere Informationen zu Singletons hier:

Also habe ich den Thread gelesen Singletons: gutes Design oder eine Krücke?
Und der Streit tobt immer noch.

Ich sehe Singletons als Designmuster (gut und schlecht).

Das Problem mit Singleton ist nicht das Pattern, sondern die Benutzer (sorry an alle). Alle und ihr Vater glauben, dass sie eines korrekt umsetzen können (und aus den vielen Interviews, die ich gemacht habe, können die meisten Leute es nicht). Da jeder denkt, dass er ein korrektes Singleton implementieren kann, missbrauchen sie das Pattern und verwenden es in Situationen, die nicht angemessen sind (indem sie globale Variablen durch Singletons ersetzen!).

Die wichtigsten Fragen, die beantwortet werden müssen, sind:

  • Wann sollten Sie einen Singleton verwenden?
  • Wie implementiere ich einen Singleton richtig?

Ich hoffe für diesen Artikel, dass wir an einem einzigen Ort eine maßgebliche Quelle für die korrekte Verwendung eines Singleton sammeln können (anstatt mehrere Websites googeln und durchsuchen zu müssen). Geeignet wäre auch eine Liste von Anti-Usages und häufig auftretenden schlechten Implementierungen, in denen erklärt wird, warum sie nicht funktionieren, und deren Schwächen bei guten Implementierungen.


Also bring den Ball ins Rollen:
Ich werde meine Hand hochhalten und sagen, dass dies das ist, was ich benutze, aber wahrscheinlich Probleme hat.
Ich mag "Scott Myers" Umgang mit dem Thema in seinen Büchern "Effective C++"

Gute Situationen, Singletons zu benutzen (nicht viele):

  • Protokollierungsframeworks
  • Thread-Recycling-Pools
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

OKAY. Lassen Sie uns gemeinsam Kritik und andere Implementierungen anbringen.
:-)

287
Martin York

Sie alle liegen falsch. Lies die Frage. Antworten:

Verwenden Sie einen Singleton, wenn:

  • Sie müssen ein und nur ein Objekt eines Typs im System haben

Verwenden Sie keinen Singleton, wenn:

  • Sie möchten Speicher sparen
  • Sie möchten etwas Neues ausprobieren
  • Sie möchten zeigen, wie viel Sie wissen
  • Weil alle anderen es tun (Siehe Frachtkult-Programmierer in Wikipedia)
  • In Widgets der Benutzeroberfläche
  • Es soll ein Cache sein
  • In Streichern
  • In Sitzungen
  • Ich kann den ganzen Tag gehen

So erstellen Sie den besten Singleton:

  • Je kleiner, desto besser. Ich bin ein Minimalist
  • Stellen Sie sicher, dass es threadsicher ist
  • Stellen Sie sicher, dass es niemals null ist
  • Stellen Sie sicher, dass es nur einmal erstellt wird
  • Lazy oder Systeminitialisierung? Bis zu Ihren Anforderungen
  • Manchmal erstellt das Betriebssystem oder die JVM Singletons für Sie (z. B. in Java jede Klassendefinition ist ein Singleton)
  • Stellen Sie einen Destruktor bereit oder finden Sie heraus, wie Sie Ressourcen entsorgen können
  • Verwenden Sie wenig Speicher
166
Javaxpert

Singletons geben Ihnen die Möglichkeit, zwei schlechte Eigenschaften in einer Klasse zu kombinieren. Das ist in jeder Hinsicht falsch.

Ein Singleton bietet Ihnen:

  1. Globaler Zugriff auf ein Objekt und
  2. Eine Garantie, dass nie mehr als ein Objekt dieses Typs erstellt werden kann

Nummer eins ist einfach. Globals sind im Allgemeinen schlecht. Wir sollten niemals Objekte global zugänglich machen, es sei denn, wir brauchen sie wirklich .

Nummer zwei mag klingen wie es Sinn macht, aber lasst uns darüber nachdenken. Wann haben Sie das letzte Mal * versehentlich * ein neues Objekt erstellt, anstatt auf ein vorhandenes zu verweisen? Da dies mit C++ markiert ist, verwenden wir ein Beispiel aus dieser Sprache. Schreibst du oft aus Versehen

std::ostream os;
os << "hello world\n";

Wann wolltest du schreiben?

std::cout << "hello world\n";

Natürlich nicht. Wir brauchen keinen Schutz gegen diesen Fehler, da dieser Fehler einfach nicht auftritt. In diesem Fall sollten Sie nach Hause gehen und 12 bis 20 Stunden schlafen und hoffen, dass Sie sich besser fühlen.

Wenn nur ein Objekt benötigt wird, erstellen Sie einfach eine Instanz. Wenn ein Objekt global verfügbar sein soll, machen Sie es zu einem globalen Objekt. Das bedeutet jedoch nicht, dass es unmöglich sein sollte, andere Instanzen davon zu erstellen.

Die Einschränkung "Nur eine Instanz ist möglich" schützt uns nicht wirklich vor wahrscheinlichen Fehlern. Aber es macht es sehr schwierig, unseren Code umzugestalten und zu warten. Weil wir ziemlich oft später herausfinden , dass wir mehr als eine Instanz brauchten. Wir haben mehr als eine Datenbank, wir haben mehr als eine Konfigurationsobjekt, wir wollen mehrere Logger. Unsere Komponententests möchten möglicherweise in der Lage sein, diese Objekte bei jedem Test zu erstellen und neu zu erstellen, um ein allgemeines Beispiel zu nennen.

Daher sollte ein Singleton genau dann verwendet werden, wenn wir beide Merkmale benötigen: Wenn wir benötigen globaler Zugriff (was selten vorkommt, da von globalen Zugriffen generell abgeraten wird) nd wir müssen verhindern Sie, dass jemand jemals mehr als eine Instanz einer Klasse erstellt (was für mich nach einem Designproblem klingt). Der einzige Grund, den ich dafür sehe, ist, dass das Erstellen von zwei Instanzen unseren Anwendungsstatus beschädigen würde - wahrscheinlich, weil die Klasse eine Reihe von statischen Elementen oder ähnliche Dummheiten enthält. In diesem Fall ist die offensichtliche Antwort, diese Klasse zu reparieren. Es sollte nicht davon abhängen, die einzige Instanz zu sein.

Wenn Sie globalen Zugriff auf ein Objekt benötigen, machen Sie es zu einem globalen Objekt wie std::cout. Beschränken Sie jedoch nicht die Anzahl der Instanzen, die erstellt werden können.

Wenn Sie die Anzahl der Instanzen einer Klasse unbedingt auf eine beschränken müssen und die Erstellung einer zweiten Instanz niemals sicher durchgeführt werden kann, müssen Sie dies erzwingen. Aber machen Sie es nicht auch global zugänglich.

Wenn Sie beide Eigenschaften brauchen, dann 1) machen Sie es zu einem Singleton, und 2) lassen Sie mich wissen, wofür Sie das brauchen, weil es mir schwer fällt, mir einen solchen Fall vorzustellen.

71
jalf

Das Problem mit Singletons ist nicht ihre Umsetzung. Es ist so, dass sie zwei unterschiedliche Konzepte miteinander verbinden, von denen keines offensichtlich wünschenswert ist.

1) Singletons bieten einen globalen Zugriffsmechanismus auf ein Objekt. Obwohl sie in Sprachen ohne eine genau definierte Initialisierungsreihenfolge geringfügig sicherer oder geringfügig zuverlässiger sind, ist diese Verwendung immer noch das moralische Äquivalent einer globalen Variablen. Es handelt sich um eine globale Variable, die in irgendeiner umständlichen Syntax (z. B. foo :: get_instance () anstelle von g_foo) dargestellt ist, aber genau denselben Zweck erfüllt (ein einzelnes Objekt, auf das über das gesamte Programm zugegriffen werden kann) und genau dieselben Nachteile aufweist.

2) Singletons verhindern mehrfache Instanziierungen einer Klasse. Es ist selten, IME, dass diese Art von Feature in einer Klasse gebacken werden sollte. Es ist normalerweise eine viel kontextuellere Sache; Viele der Dinge, die als eins und nur eins angesehen werden, sind wirklich nur zufällig eins. IMO ist es besser, nur eine Instanz zu erstellen - bis Sie feststellen, dass Sie mehr als eine Instanz benötigen.

35
DrPizza

Eine Sache mit Mustern: nicht verallgemeinern. Sie haben alle Fälle, in denen sie nützlich sind und wenn sie versagen.

Singleton kann böse sein, wenn Sie den Code testen müssen. Sie sind im Allgemeinen an eine Instanz der Klasse gebunden und können zwischen dem Öffnen einer Tür im Konstruktor oder einer Methode zum Zurücksetzen des Status usw. wählen.

Ein anderes Problem ist, dass der Singleton in der Tat nichts anderes ist als eine globale Variable in Verkleidung. Wenn Sie zu viele gemeinsame Zustände in Ihrem Programm haben, gehen die Dinge tendenziell zurück, wir alle wissen es.

Dies kann Abhängigkeitsverfolgung erschweren. Wenn alles von Ihrem Singleton abhängt, ist es schwieriger, es zu ändern, in zwei zu teilen usw. Sie bleiben im Allgemeinen dabei. Dies beeinträchtigt auch die Flexibilität. Untersuchen Sie ein Dependency Injection Framework, um dieses Problem zu beheben.

26
Paweł Hajdan

Singletons lassen Sie im Grunde einen komplexen globalen Zustand in Sprachen haben, die es sonst schwierig oder unmöglich machen, komplexe globale Variablen zu haben.

Insbesondere Java verwendet Singletons als Ersatz für globale Variablen, da alles in einer Klasse enthalten sein muss. Globalen Variablen sind öffentliche statische Variablen am nächsten, die so verwendet werden können, als wären sie global mit import static

C++ hat zwar globale Variablen, aber die Reihenfolge, in der Konstruktoren globaler Klassenvariablen aufgerufen werden, ist nicht definiert. Daher können Sie mit einem Singleton die Erstellung einer globalen Variablen verschieben, bis diese Variable zum ersten Mal benötigt wird.

Sprachen wie Python und Ruby verwenden Singletons sehr selten, da Sie stattdessen globale Variablen innerhalb eines Moduls verwenden können.

Wann ist es also gut/schlecht, einen Singleton zu verwenden? Ziemlich genau, wann es gut/schlecht wäre, eine globale Variable zu verwenden.

12
Eli Courtwright
  • Wie implementiere ich einen Singleton richtig?

Es gibt ein Problem, das ich noch nie erwähnt habe, etwas, auf das ich bei einem früheren Job gestoßen bin. Wir hatten C++ - Singletons, die von DLLs gemeinsam genutzt wurden, und die üblichen Mechanismen, um sicherzustellen, dass eine einzelne Instanz einer Klasse funktioniert, funktionieren einfach nicht. Das Problem ist, dass jede DLL zusammen mit der EXE-Datei einen eigenen Satz statischer Variablen erhält. Wenn Ihre get_instance-Funktion inline ist oder Teil einer statischen Bibliothek ist, wird jede DLL mit einer eigenen Kopie des "Singleton" beendet.

Die Lösung besteht darin, sicherzustellen, dass der Singleton-Code nur in einer DLL oder EXE-Datei definiert ist, oder einen Singleton-Manager mit diesen Eigenschaften zu erstellen, um Instanzen zu parzellieren.

6
Mark Ransom

Modern C++ Design von Alexandrescu verfügt über einen thread-sicheren, vererbbaren generischen Singleton.

Für meinen 2p-Wert halte ich es für wichtig, Lebensdauern für Ihre Singuletts zu definieren (wenn es unbedingt erforderlich ist, diese zu verwenden). Normalerweise lasse ich die statische Funktion get() nichts instanziieren und überlasse die Einrichtung und Zerstörung einem bestimmten Abschnitt der Hauptanwendung. Dies hilft, Abhängigkeiten zwischen Singletons hervorzuheben - aber wie oben betont, ist es am besten, sie nur zu vermeiden, wenn dies möglich ist.

6
tenpn

Das erste Beispiel ist nicht threadsicher - wenn zwei Threads gleichzeitig getInstance aufrufen, wird diese Statik zu einer PITA. Irgendeine Form von Mutex würde helfen.

5
Rob

Wie andere angemerkt haben, umfassen die Hauptnachteile von Singletons die Unfähigkeit, sie zu erweitern, und den Verlust der Fähigkeit, mehr als eine Instanz zu instantiieren, z. zu Testzwecken.

Einige nützliche Aspekte von Singletons:

  1. lazy oder Upfront Instantiierung
  2. praktisch für ein Objekt, das Setup und/oder Status erfordert

Sie müssen jedoch keinen Singleton verwenden, um diese Vorteile zu erzielen. Sie können ein normales Objekt schreiben, das die Arbeit erledigt, und dann über eine Fabrik (ein separates Objekt) Zugriff darauf haben. Die Fabrik kann sich darum kümmern, nur eine Instanz zu instanziieren und bei Bedarf wiederzuverwenden. Wenn Sie auf eine Schnittstelle anstatt auf eine konkrete Klasse programmieren, kann die Factory Strategien verwenden, d. H. Sie können verschiedene Implementierungen der Schnittstelle ein- und ausschalten.

Schließlich eignet sich eine Fabrik für Abhängigkeitseinspritztechnologien wie Spring usw.

4
lexh

Da mit einem Singleton nur eine Instanz erstellt werden kann, wird die Instanzreplikation effektiv gesteuert. Zum Beispiel würden Sie nicht mehrere Instanzen eines Lookups benötigen - eine Morse-Lookup-Map zum Beispiel, daher ist es passend, sie in eine Singleton-Klasse zu packen. Und nur weil Sie eine einzelne Instanz der Klasse haben, bedeutet dies nicht, dass Sie auch auf die Anzahl der Verweise auf diese Instanz beschränkt sind. Sie können Anrufe (um Threading-Probleme zu vermeiden) in die Warteschlange stellen und die erforderlichen Änderungen vornehmen. Ja, die allgemeine Form eines Singletons ist global öffentlich. Sie können das Design jedoch ändern, um ein Singleton mit eingeschränkter Zugriffsberechtigung zu erstellen. Ich habe das noch nie müde gemacht, aber ich weiß, dass es möglich ist. Und all jenen, die kommentiert haben, das Singleton-Muster sei absolut böse, sollten Sie das wissen: Ja, es ist böse, wenn Sie es nicht richtig verwenden oder sich darin auf effektive Funktionalität und vorhersehbares Verhalten beschränken: NICHT GENERALISIEREN.

3
gogole

Singletons sind praktisch, wenn beim Initialisieren und Widersprechen viel Code ausgeführt wird. Wenn Sie zum Beispiel iBatis verwenden, wenn Sie ein Persistenzobjekt einrichten, muss es alle Konfigurationen lesen, die Karten analysieren, sicherstellen, dass alles korrekt ist, usw., bevor Sie zu Ihrem Code gelangen.

Wenn Sie dies jedes Mal tun, wird die Leistung stark beeinträchtigt. Wenn Sie es in einem Singleton verwenden, nehmen Sie diesen Treffer einmal und alle nachfolgenden Aufrufe müssen ihn nicht ausführen.

3
Brian

Die meisten Menschen verwenden Singletons, wenn sie versuchen, sich bei der Verwendung einer globalen Variablen wohl zu fühlen. Es gibt legitime Verwendungen, aber die Tatsache, dass es nur eine Instanz geben kann, ist meistens nur eine triviale Tatsache im Vergleich zu der Tatsache, dass sie global verfügbar ist.

3
Brad Barker

Der wirkliche Untergang von Singletons ist, dass sie das Erbe brechen. Sie können keine neue Klasse ableiten, um erweiterte Funktionen zu erhalten, es sei denn, Sie haben Zugriff auf den Code, auf den sich der Singleton bezieht. Abgesehen von der Tatsache, dass der Singleton Ihren Code eng miteinander verknüpft (durch ein Strategiemuster fixierbar ... auch bekannt als Dependency Injection), hindert er Sie auch daran, Teile des Codes vor einer Revision zu schützen (gemeinsam genutzte Bibliotheken).

Daher sind auch die Beispiele für Logger oder Thread-Pools ungültig und sollten durch Strategien ersetzt werden.

3
ZebZiggle

Aber wenn ich so etwas wie einen Singleton brauche, benutze ich oft einen Schwarz Counter , um ihn zu instanziieren.

2
Matt Cruikshank

Ich benutze Singletons als Interviewtest.

Wenn ich einen Entwickler auffordere, einige Entwurfsmuster zu benennen, werden sie nicht eingestellt, wenn sie nur Singleton nennen können.

1
Matt Cruikshank

Im Folgenden finden Sie den besseren Ansatz zum Implementieren eines thread-sicheren Singleton-Musters mit Freigabe des Speichers im Destruktor. Ich denke jedoch, dass der Destruktor optional sein sollte, da die Singleton-Instanz automatisch zerstört wird, wenn das Programm beendet wird:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

In Bezug auf die Situationen, in denen wir Singleton-Klassen verwenden müssen, können wir den Zustand der Instanz während der Ausführung des Programms beibehalten, wenn wir in das Ausführungsprotokoll einer Anwendung schreiben, in der nur eine Instanz der Datei erforderlich ist benutzt werden .... und so weiter. Es ist sehr hilfreich, wenn jemand in meinem obigen Code eine Optimierung vorschlagen kann.

1
A. Gupta

Das Meyers-Singleton-Muster funktioniert die meiste Zeit gut genug, und gelegentlich lohnt es sich nicht unbedingt, nach etwas Besserem zu suchen. Solange der Konstruktor niemals wirft und es keine Abhängigkeiten zwischen Singletons gibt.

Ein Singleton ist eine Implementierung für ein global zugängliches Objekt (GAO von nun an), obwohl nicht alle GAOs Singletons sind.

Die Protokollierer selbst sollten keine Singletons sein, aber die Mittel zum Protokollieren sollten idealerweise global verfügbar sein, um zu entkoppeln, wo und wie die Protokollnachricht erstellt wird.

Lazy-Loading/Lazy Evaluation ist ein anderes Konzept und Singleton implementiert dies normalerweise auch. Es bringt viele eigene Probleme mit sich, insbesondere Thread-Sicherheit und Probleme, wenn es mit Ausnahmen scheitert, so dass sich herausstellt, dass das, was zu dieser Zeit als gute Idee erschien, doch nicht so großartig ist. (Ein bisschen wie die COW-Implementierung in Strings).

In diesem Sinne können GOAs folgendermaßen initialisiert werden:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

In einer geladenen Bibliothek, die Objekte enthält, müssen Sie wahrscheinlich einen anderen Mechanismus verwenden, um deren Lebensdauer zu verwalten. (Legen Sie sie in ein Objekt, das Sie beim Laden der Bibliothek erhalten).

Wann verwende ich Singletons? Ich habe sie für zwei Dinge verwendet: Eine Singleton-Tabelle, die angibt, welche Bibliotheken mit dlopen geladen wurden. Eine Nachrichtenbehandlungsroutine, die von Protokollierern abonniert und an die Sie Nachrichten senden können. Wird speziell für Signalhandler benötigt.

0
CashCow

Wenn Sie derjenige sind, der das Singleton erstellt hat und es verwendet, machen Sie es nicht als Singleton (es hat keinen Sinn, weil Sie die Singularität des Objekts steuern können, ohne es als Singleton zu definieren), aber es ist sinnvoll, wenn Sie ein Entwickler von a sind Bibliothek und Sie möchten Ihren Benutzern nur ein Objekt zur Verfügung stellen (in diesem Fall haben Sie den Singleton erstellt, aber Sie sind nicht der Benutzer).

Singletons sind Objekte. Verwenden Sie sie also als Objekte. Viele Benutzer greifen direkt auf Singletons zu, indem sie die Methode aufrufen, die sie zurückgibt. Dies ist jedoch schädlich, da Sie Ihrem Code mitteilen, dass es sich bei dem Objekt um Singleton handelt. Ich bevorzuge Singletons als Objekte. Ich übergebe sie Durch den Konstruktor und die Verwendung als gewöhnliche Objekte weiß Ihr Code auf diese Weise nicht, ob diese Objekte Singletons sind oder nicht, und das macht die Abhängigkeiten klarer und hilft ein wenig beim Refactoring.

0
La VloZ Merrill

Anti-Usage:

Ein Hauptproblem bei übermäßiger Verwendung von Singleton besteht darin, dass das Muster die einfache Erweiterung und den Austausch alternativer Implementierungen verhindert. Der Klassenname ist überall dort, wo der Singleton verwendet wird, fest codiert.

0
Adam Franco

Ich finde sie nützlich, wenn ich eine Klasse habe, die viel Speicher einschließt. In einem Spiel, an dem ich kürzlich gearbeitet habe, habe ich beispielsweise eine Influence-Map-Klasse, die eine Sammlung sehr großer Arrays zusammenhängenden Speichers enthält. Ich möchte, dass alle beim Start zugewiesen, alle beim Herunterfahren freigegeben und ich möchte auf jeden Fall nur eine Kopie davon. Ich muss auch von vielen Orten darauf zugreifen. Ich finde das Singleton-Muster in diesem Fall sehr nützlich.

Ich bin sicher, dass es andere Lösungen gibt, aber ich finde diese sehr nützlich und einfach zu implementieren.

0

Ich denke, dies ist die robusteste Version für C #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Hier ist die . NET-optimierte Version :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Sie finden dieses Muster unter dotfactory.com .

0
artur02

Ich verstehe immer noch nicht, warum ein Singleton global sein muss.

Ich wollte einen Singleton erstellen, in dem ich eine Datenbank in der Klasse als private konstante statische Variable versteckte und Klassenfunktionen erstellte, die die Datenbank nutzten, ohne die Datenbank jemals dem Benutzer zugänglich zu machen.

Ich verstehe nicht, warum diese Funktionalität schlecht wäre.

0
Zachary Kraus