Wann sollten Sie das Singleton-Muster anstelle einer statischen Klasse verwenden?[geschlossen]

StackOverflow https://stackoverflow.com/questions/46541

  •  09-06-2019
  •  | 
  •  

Frage

Nennen Sie die Designüberlegungen bei der Entscheidung zwischen der Verwendung von a Singleton im Vergleich zu einer statischen Klasse.Dabei sind Sie gewissermaßen gezwungen, die beiden gegenüberzustellen, sodass alle Kontraste, die Ihnen einfallen, auch nützlich sind, um Ihren Denkprozess darzustellen!Außerdem sieht jeder Interviewer gerne anschauliche Beispiele.:) :)

War es hilfreich?

Lösung

  • Singletons können Schnittstellen implementieren und von anderen Klassen erben.
  • Singletons können verzögert geladen werden.Nur dann, wenn es tatsächlich benötigt wird.Das ist sehr praktisch, wenn die Initialisierung teures Laden von Ressourcen oder Datenbankverbindungen beinhaltet.
  • Singletons bieten ein tatsächliches Objekt.
  • Singletons können zu einer Fabrik erweitert werden.Die Objektverwaltung hinter den Kulissen ist abstrakt, sodass sie besser wartbar ist und zu besserem Code führt.

Andere Tipps

Wie wäre es mit „beides vermeiden“?Singletons und statische Klassen:

  • Kann einen globalen Staat einführen
  • Lassen Sie sich eng mit mehreren anderen Klassen verknüpfen
  • Abhängigkeiten ausblenden
  • Kann isolierte Unit-Test-Klassen erschweren

Schauen Sie stattdessen hinein Abhängigkeitsspritze Und Umkehrung des Kontrollcontainers Bibliotheken.Mehrere der IoC-Bibliotheken übernehmen die Lebensdauerverwaltung für Sie.

(Wie immer gibt es Ausnahmen, z. B. statische Mathematikklassen und C#-Erweiterungsmethoden.)

Ich würde behaupten, der einzige Unterschied ist die Syntax:MySingleton.Current.Whatever() vs. MySingleton.Whatever().Der Staat ist, wie David erwähnte, in beiden Fällen letztlich „statisch“.


BEARBEITEN:Die Beerdigungsbrigade kam vom Graben herüber ...Jedenfalls dachte ich an einen Fall, der einen Singleton erfordern würde.Statische Klassen können weder von einer Basisklasse erben noch eine Schnittstelle implementieren (zumindest in .Net ist dies nicht möglich).Wenn Sie diese Funktionalität benötigen, müssen Sie einen Singleton verwenden.

Eine meiner Lieblingsdiskussionen zu diesem Thema ist Hier (Originalseite nicht verfügbar, jetzt verlinkt Internet Archive Wayback Machine.)

Um die Flexibilitätsvorteile eines Singleton zusammenzufassen:

  • Ein Singleton kann leicht in eine Fabrik umgewandelt werden
  • Ein Singleton kann leicht modifiziert werden, um verschiedene Unterklassen zurückzugeben
  • Dies kann zu einer besser wartbaren Anwendung führen

Eine statische Klasse mit einer Menge statischer Variablen ist ein kleiner Hack.

/**
 * Grotty static semaphore
 **/
 public static class Ugly {

   private static int count;

   public synchronized static void increment(){
        count++;
   }

   public synchronized static void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized static boolean isClear(){
         return count==0;    

    }
   }

Ein Singleton mit einer tatsächlichen Instanz ist besser.

/**
 * Grotty static semaphore
 **/
 public static class LessUgly {
   private static LessUgly instance;

   private int count;

   private LessUgly(){
   }

   public static synchronized getInstance(){
     if( instance==null){
        instance = new LessUgly();
     }
     return instance;
   }
   public synchronized void increment(){
        count++;
   }

   public synchronized void decrement(){
        count--;
        if( count<0 ) {
            count=0;
        }
   }

   public synchronized boolean isClear(){
         return count==0;    

    }
   }

Der Zustand liegt NUR in der Instanz vor.

So kann der Singleton später geändert werden, um Pooling, Thread-lokale Instanzen usw. durchzuführen.Und keiner der bereits geschriebenen Codes muss geändert werden, um den Vorteil zu nutzen.

public static class LessUgly {
       private static Hashtable<String,LessUgly> session;
       private static FIFO<LessUgly> freePool = new FIFO<LessUgly>();
       private static final POOL_SIZE=5;
       private int count;

       private LessUgly(){
       }

       public static synchronized getInstance(){
         if( session==null){
            session = new Hashtable<String,LessUgly>(POOL_SIZE);
            for( int i=0; i < POOL_SIZE; i++){
               LessUgly instance = new LessUgly();  
               freePool.add( instance)
            }
         }
         LessUgly instance = session.get( Session.getSessionID());
         if( instance == null){
            instance = freePool.read();
         }
         if( instance==null){
             // TODO search sessions for expired ones. Return spares to the freePool. 
             //FIXME took too long to write example in blog editor.
         }
         return instance;
       }     

Es ist möglich, etwas Ähnliches mit einer statischen Klasse zu tun, aber beim indirekten Versand entsteht pro Aufruf ein Mehraufwand.

Sie können die Instanz abrufen und als Argument an eine Funktion übergeben.Dadurch kann Code an den „richtigen“ Singleton weitergeleitet werden.Wir wissen, dass Sie nur eines davon brauchen werden ...bis du es nicht mehr tust.

Der große Vorteil besteht darin, dass zustandsbehaftete Singletons threadsicher gemacht werden können, eine statische Klasse hingegen nicht, es sei denn, Sie ändern sie in einen geheimen Singleton.

Stellen Sie sich einen Singleton wie einen Dienst vor.Es handelt sich um ein Objekt, das eine bestimmte Funktionalität bereitstellt.Z.B.

ObjectFactory.getInstance().makeObject();

Die Objektfabrik ist ein Objekt, das einen bestimmten Dienst ausführt.

Im Gegensatz dazu ist eine Klasse voller statischer Methoden eine Sammlung von Aktionen, die Sie möglicherweise ausführen möchten, organisiert in einer verwandten Gruppe (der Klasse).Z.B.

StringUtils.reverseString("Hello");
StringUtils.concat("Hello", "World");

Das StringUtils-Beispiel hier ist eine Sammlung von Funktionen, die überall angewendet werden können.Das Singleton-Factory-Objekt ist ein spezifischer Objekttyp mit einer klaren Verantwortung, der bei Bedarf erstellt und weitergegeben werden kann.

Statische Klassen werden zur Laufzeit instanziiert.Dies kann zeitaufwändig sein.Singletons können nur bei Bedarf instanziiert werden.

Singletons sollten nicht auf die gleiche Weise wie statische Klassen verwendet werden.Im Wesentlichen,

MyStaticClass.GetInstance().DoSomething();

ist im Wesentlichen dasselbe wie

MyStaticClass.DoSomething();

Was Sie eigentlich tun sollten, ist Behandeln Sie den Singleton einfach als ein weiteres Objekt.Wenn ein Dienst eine Instanz des Singleton-Typs erfordert, übergeben Sie diese Instanz im Konstruktor:

var svc = new MyComplexServce(MyStaticClass.GetInstance());

Der Dienst sollte sich nicht darüber im Klaren sein, dass es sich bei dem Objekt um ein Singleton handelt, und sollte das Objekt lediglich als Objekt behandeln.

Das Objekt kann durchaus als Implementierungsdetail und als Aspekt der Gesamtkonfiguration oder als Singleton implementiert werden, wenn das die Sache einfacher macht.Aber die Dinge, die das Objekt verwenden, sollten nicht wissen müssen, ob das Objekt ein Singleton ist oder nicht.

Wenn Sie mit „statischer Klasse“ eine Klasse meinen, die nur statische Variablen hat, dann können sie tatsächlich den Zustand beibehalten.Ich verstehe, dass der einzige Unterschied darin besteht, wie Sie auf dieses Ding zugreifen.Zum Beispiel:

MySingleton().getInstance().doSomething();

gegen

MySingleton.doSomething();

Die Interna von MySingleton unterscheiden sich offensichtlich voneinander, aber abgesehen von Thread-Sicherheitsproblemen werden beide in Bezug auf den Client-Code die gleiche Leistung erbringen.

Das Singleton-Muster wird im Allgemeinen verwendet, um instanzunabhängige oder statische Daten zu bedienen, bei denen mehrere Threads gleichzeitig auf Daten zugreifen können.Ein Beispiel können Landescodes sein.

Singletons sollten niemals verwendet werden (es sei denn, Sie betrachten eine Klasse ohne veränderlichen Zustand als Singleton).„Statische Klassen“ sollten keinen veränderlichen Zustand haben, außer vielleicht threadsicheren Caches und dergleichen.

So ziemlich jedes Beispiel eines Singletons zeigt, wie man es nicht macht.

Wenn Sie einen Singleton entsorgen können, um ihn anschließend zu bereinigen, können Sie ihn in Betracht ziehen, wenn es sich um eine begrenzte Ressource handelt (z. B.(nur 1 davon), die Sie nicht ständig benötigen und bei deren Zuweisung gewisse Speicher- oder Ressourcenkosten anfallen.

Der Bereinigungscode sieht natürlicher aus, wenn Sie einen Singleton haben, im Gegensatz zu einer statischen Klasse, die statische Statusfelder enthält.

Der Code wird jedoch in beiden Fällen ungefähr gleich aussehen. Wenn Sie also konkretere Gründe für die Anfrage haben, sollten Sie dies vielleicht näher erläutern.

Die beiden können ziemlich ähnlich sein, aber denken Sie daran, dass es der wahre Singleton sein muss selbst instanziiert (einmal gewährt) und dann bereitgestellt werden.Eine PHP-Datenbankklasse, die eine Instanz von zurückgibt mysqli ist nicht wirklich ein Singleton (wie manche Leute es nennen), weil es eine Instanz einer anderen Klasse zurückgibt, nicht eine Instanz der Klasse, die die Instanz als statisches Mitglied hat.

Wenn Sie also eine neue Klasse schreiben, von der Sie nur eine Instanz in Ihrem Code zulassen möchten, können Sie sie genauso gut als Singleton schreiben.Stellen Sie sich das so vor, als würden Sie eine Plain-Jane-Klasse schreiben und diese erweitern, um die Anforderung der Einzelinstanziierung zu erleichtern.Wenn Sie die Klasse einer anderen Person verwenden, die Sie nicht ändern können (z. B mysqli), sollten Sie eine statische Klasse verwenden (auch wenn Sie der Definition kein Schlüsselwort voranstellen).

Singletons sind flexibler, was in Fällen nützlich sein kann, in denen die Instanzmethode basierend auf einem bestimmten Kontext verschiedene konkrete Unterklassen des Singleton-Typs zurückgeben soll.

Statische Klassen können nicht als Argumente weitergegeben werden;Instanzen eines Singletons können sein.Achten Sie, wie in anderen Antworten erwähnt, auf Threading-Probleme mit statischen Klassen.

rp

Ein Singleton kann einen Konstruktor und einen Destruktor haben.Abhängig von Ihrer Sprache wird der Konstruktor möglicherweise automatisch aufgerufen, wenn Ihr Singleton zum ersten Mal verwendet wird, oder nie, wenn Ihr Singleton überhaupt nicht verwendet wird.Eine statische Klasse hätte keine solche automatische Initialisierung.

Sobald ein Verweis auf ein Singleton-Objekt erhalten wurde, kann es wie jedes andere Objekt verwendet werden.Der Client-Code muss möglicherweise nicht einmal wissen, dass er einen Singleton verwendet, wenn zuvor ein Verweis auf den Singleton gespeichert wurde:

Foo foo = Foo.getInstance();
doSomeWork(foo); // doSomeWork wont even know Foo is a singleton

Dies macht die Sache natürlich einfacher, wenn Sie das Singleton-Muster zugunsten eines echten Musters wie IoC aufgeben.

Verwenden Sie das Singleton-Muster, wenn Sie zur Laufzeit etwas berechnen müssen, das Sie, wenn Sie könnten, zur Kompilierungszeit berechnen würden, z. B. Nachschlagetabellen.

Ich denke, dass Singleton sinnvoller ist als die statische Klasse, wenn Sie einen Pool kostspieliger Ressourcen (wie Datenbankverbindungen) aufbauen müssen.Sie wären nicht daran interessiert, den Pool zu erstellen, wenn niemand ihn jemals nutzt (statische Klassen bedeuten, dass Sie die kostspielige Arbeit erledigen, wenn die Klasse geladen wird).

Ein Singleton ist auch eine gute Idee, wenn Sie ein effizientes Caching von Daten erzwingen möchten.Ich habe zum Beispiel eine Klasse, die Definitionen in einem XML-Dokument nachschlägt.Da das Parsen des Dokuments eine Weile dauern kann, habe ich einen Cache mit Definitionen eingerichtet (ich verwende SoftReferences, um outOfmemeoryErrors zu vermeiden).Wenn die gewünschte Definition nicht im Cache ist, mache ich das teure XML-Parsing.Ansonsten gebe ich eine Kopie aus dem Cache zurück.Da mehrere Caches bedeuten würden, dass ich möglicherweise immer noch dieselbe Definition mehrmals laden müsste, benötige ich einen statischen Cache.Ich habe mich dafür entschieden, diese Klasse als Singleton zu implementieren, damit ich die Klasse nur mit normalen (nicht statischen) Datenelementen schreiben kann.Dadurch kann ich immer noch eine Instanziierung der Klasse erstellen, falls ich sie aus irgendeinem Grund benötige (Serialisierung, Unit-Tests usw.).

Singleton ist, wie bereits erwähnt, wie ein Dienst.Pro ist seine Flexibilität.Statisch, nun, Sie benötigen einige statische Teile, um Singleton zu implementieren.

Singleton verfügt über Code, der sich um die Instanziierung des tatsächlichen Objekts kümmert, was eine große Hilfe sein kann, wenn Sie auf Rennprobleme stoßen.In einer statischen Lösung müssen Sie möglicherweise Rennprobleme an mehreren Codestellen behandeln.

Da Singleton jedoch mit einigen statischen Variablen erstellt werden kann, können Sie es möglicherweise mit „goto“ vergleichen.Es kann für den Bau anderer Strukturen sehr nützlich sein, aber Sie müssen wirklich wissen, wie man es verwendet, und sollten es nicht „überbeanspruchen“.Daher ist die allgemeine Empfehlung, bei Singleton zu bleiben und bei Bedarf Static zu verwenden.

Schauen Sie sich auch den anderen Beitrag an: Warum eine statische Klasse einer Singleton-Implementierung vorziehen?

verweisen Das

Zusammenfassung:

A.Eine einfache Faustregel, die Sie befolgen können: Wenn der Status nicht aufrechterhalten werden muss, können Sie eine statische Klasse verwenden. Andernfalls sollten Sie eine Singleton-Klasse verwenden.

B.Verwenden Sie einen Singleton, wenn es sich um ein besonders „schweres“ Objekt handelt.Wenn Ihr Objekt groß ist und eine angemessene Menge an Speicher beansprucht, sind viele N/W-Aufrufe (Verbindungspool) usw. erforderlich.Um sicherzustellen, dass es nicht mehrmals instanziiert wird.Eine Singleton-Klasse hilft dabei, zu verhindern, dass ein solcher Fall jemals eintritt

Wenn die einzelne Klasse einen Status benötigt.Singletons behalten einen globalen Zustand bei, statische Klassen nicht.

Erstellen Sie beispielsweise einen Helfer für eine Registrierungsklasse:Wenn Sie einen veränderbaren Hive haben (HKey Current User vs.HKEY Local Machine) können Sie Folgendes tun:

RegistryEditor editor = RegistryEditor.GetInstance();
editor.Hive = LocalMachine

Jetzt werden alle weiteren Aufrufe dieses Singletons auf der Struktur der lokalen Maschine ausgeführt.Andernfalls müssten Sie bei Verwendung einer statischen Klasse angeben, dass die lokale Maschine jeden Tag hive, oder über eine Methode wie z ReadSubkeyFromLocalMachine.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top