Autor Beitrag
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: So 01.11.15 15:44 
Moin!

Ihr lieben Mitstreiter an der Wissensfront, ;) ich hätte da gern mal wieder ein Problem. :P

Seit einiger Zeit beschäftigen mich Gedanken zum Singleton-Pattern in PHP, ein ziemlich guter Einstieg ist (leider nicht bei uns :( sondern) hier.

Wenn ich mir statische Eigenschaften und Methoden ansehe, dann klingt das für mich erstmal so, als bräuchte ich kein Singleton-Pattern in PHP. :nixweiss: Gut, ich kann in statischen Methoden nicht auf Klasseneigenschaften zugreifen ($this gibt´s ja nicht), aber das will ich ja auch eigentlich gar nicht. :nixweiss:

Ich wäre sehr interessiert an euren Gedanken zu dem Thema, und vielleicht hat ja sogar jemand ein paar passende/spannende Beispiele parat. :les: :think:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
FinnO
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1331
Erhaltene Danke: 123

Mac OSX, Arch
TypeScript (Webstorm), Kotlin, Clojure (IDEA), Golang (VSCode)
BeitragVerfasst: So 01.11.15 19:10 
Moin Narses,

Zitat:
user profile iconNarses"(696616)]Seit einiger Zeit beschäftigen mich Gedanken zum Singleton-Pattern

Wie überall im Leben gibt es hier stark verschiedene Meinungen zu. Viele betrachten das Singleton-Pattern eher als ein Anti-Pattern ;). Trotzdem ist Singleton an sich ein doch eher verbreitetes Pattern, wobei ich damit eher in einem Softwarearchäologischen Kontext damit Erfahrung gemacht habe, die nämlich im wesentlichen darin besteht, ein Singleton irgendwo wieder herauszupulen.

Mein größtes Problem mit dem Singleton-Pattern ist, dass man sich überall und an jeder Stelle tightly an das Singleton coupled (#buzzwordBingo). Ich liefere mal ein Beispiel (bin leider nicht fließend genug in PHP, aber das sollte ja keine Rolle spielen...):

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

public class MyCoupledClass 
{
  public void DoSomethingWithTheSingleton()
  {
    Singleton.Instance.DoSth();
  }
};


Durch die Verwendung von Singleton.Instance (analog natürlich Singleton::Instance() in PHP), führt man sich eine Abhängigkeit zwischen MyCoupledClass und Singleton ein, die man so ohne weiteres nicht erkennen kann - sie ist nämlich nicht wirklich zu sehen, ohne in den Funktionsbody zu schauen. Möchte ich jetzt in Zukunft eins der folgenden Szenarien abdecken:


  • Ich benötige doch plötzlich mehrere Objekte vom Typ Singleton
  • Ich möchte meiner Anwendung Unit-Tests hinzufügen
  • Ich möchte Polymorphie nutzen und unter gewissen Bedingungen (d.h. nicht immer) eine andere Implementierung von Singleton zur Verfügung stellen


habe ich den Salat und muss potentiell viele Zeilen Code bearbeiten. Das ist teuer und macht zudem noch keinen Spaß ;).

Besser ist es daher meiner Meinung nach, eigentlich für jeden Fall eine Form der Dependency-Injection zu verwenden. Auch hier kann man den gleichen Effekt erreichen, wie mit einem Singleton (nämlich, dass nur eine Instanz einer Klasse existieren kann), jedoch lässt sich dieses Pattern innerhalb kürzester Zeit umbauen, sodass die o.g. Anforderungen erfüllbar sind. Ich stelle hier einmal Constructor Injection vor, die eine von eine Factory generierte, einzigartige Instanz einer Klasse in eine Abhängige Klasse injiziert, vor (#buzzwordBingo again ;)).

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
public interface IMyInterface
{

};

public class MyClass : IMyInterface
{
  //Hier ist es theoretisch auch möglich, den Constructor private zu machen und 
  //die Factory als friend class aufzuführen. Der Gewinn ist meiner Meinung nach
  //aber nicht wirklich gegeben, es sei denn, man muss wirklich auf Gedeih und 
  //Verderb verhindern, dass jemand eine neue Instanz von MyClass erzeugt...
  public MyClass(){}; 
};

public class Factory 
{
  private MyClass _myInstance;  
  public Factory()
  {
    _myInstance = new MyClass();  
  }

  public IMyInterface GetMyInterface()
  {
    return _myInstance;
  }
}

public class MyDependantClass
{
  private IMyInterface _iNeedThis;
  public MyDependantClass(IMyInterface dependency)
  {
    _iNeedThis = dependency;
  }
}

public void Main()
{
  var f = new Factory();
  // m und m2 nutzen beide die gleiche Instanz von MyClass. 
  var m = new MyDependantClass(f.GetMyInterface());
  var m2 = new MyDependantClass(f.GetMyInterface()); 
}


Natürlich ist dieses Beispiel noch nicht das höchste der Gefühle, da hier noch unnötige Abhängigkeiten von der Factory bestehen und man zur Änderung der Implementierung hier die Factory anfassen muss. Durch geschickte Implementierung lässt sich hier noch eine Abstraktionsschicht einführen - dafür empfiehlt es sich aber, mal den Code oder zumindest die API eines Dependency-Injection Frameworks anzuschauen. Trotzdem wird im wesentlichen das erreicht, was ein Singleton kann. Trotzdem kann ich (zum Beispiel für Tests) die Implementierung des IMyInterface, welches für MyDependantClass genutzt wird, an genau der Stelle ändern, an der ich die Klasse erzeuge.

Ich hoffe, ich konnte etwas helfen.
Gruß
Finn


Zuletzt bearbeitet von FinnO am Mo 02.11.15 22:45, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: Narses
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: So 01.11.15 21:55 
Singleton ist ganz sicher ein gefährliches Pattern und so wie ich das sehe auch ein Anti-Pattern, allerdings nicht unnütz.
Wichtig ist nur, dass man sich bei der Anwendung sehr genau überlegt, ob es tatsächlich notwendig ist und sich nicht anders lösen lässt.

In den meisten Fällen lässt sich das auch über eine Factory lösen und die Nachteile vom Singleton sind dann gelöst.
Manchmal nutze ich aber auch beides Zusammen: Ein Singleton für die Factory.
Sinnvoll ist das dann, wenn die Factory diverse Vorbereitungen treffen muss und dabei sehr teure Objekte erzeugt und gecached werden.
Ein solcher Vorgang ist in den meisten Fällen nur einmal notwendig.

Wir haben in der Firma z.B. einige Bereiche, die sehr viel mit Reflection oder sogar mit Reflection.Emit arbeiten müssen. Da geht viel Zeit flöten, was es zu vermeiden gilt, daher werden die aufwendigen Parts beim Anwendungsstart in einen Thread geschoben, damit sie dort in Ruhe vor sich hin laufen können, bis sie dann gebraucht werden. Die Vorbereitungen sind dann als Singleton erreichbar.
Das Ergebnis: Der sehr zeitaufwendige Ablauf ist überhaubt nicht spürbar und stellt damit eine enorme Performance-Verbesserung dar.

Ich nutze auch gerne ein Singleton für eine Factory, wenn diese Factory das Dependency-Injection-Pattern umsetzen soll ^^
Die lädt dann einmal am Anfang die ganzen Abhängigkeiten und hält sie dann bereit, bis sie gebraucht werden.


Ein anderes Beispiel für ein Singleton:
Eine global in der Anwendung existierende Datenbank-Verbindung.
Das Öffnen und Schließen ist teuer und wenn sehr häufig Datenbank-Interaktionen notwendig sind, dann könnte es sinnvoll sein, diese Verbindung hinter einem Singleton zu halten.
Oder wenn dem User ein Anmelde-Dialog angezeigt werden soll, dann sollte das einmalig und die Session im Hintergrund aktiv bleiben.
Dazu noch zwei Methoden Open und Close, Open wird beim Starten aufgerufen, Close beim Beenden der Anwendung.




Das Singleton sollte die letzte Wahl sein, wenn z.B. eine Factory nicht aushelfen kann.
Aber vergesst dabei nicht: Andere Patterns haben auch ihre Nachteile, die vielleicht in dem konkreten Fall schwerer wiegen.
Die Factory z.B. stellt eine ebenso starke Abhängigkeit dar, wie das Singleton.

Für diesen Beitrag haben gedankt: Narses
FinnO
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1331
Erhaltene Danke: 123

Mac OSX, Arch
TypeScript (Webstorm), Kotlin, Clojure (IDEA), Golang (VSCode)
BeitragVerfasst: Mo 02.11.15 22:51 
user profile iconPalladin007 hat folgendes geschrieben Zum zitierten Posting springen:
Die Factory z.B. stellt eine ebenso starke Abhängigkeit dar, wie das Singleton.


Ein Stück weit würde ich hier schon noch widersprechen wollen. Der elementare Unterschied ist nämlich, dass man sich durch das Singleton in der Regel eine Abhängigkeit von einer statischen Methode hereinholt (die wie gesagt nur im Funktionsbody sichtbar ist), während man die Factory geschickt nutzen kann um die Instanz der gewünschten Klasse in die abhängige Klasse zu injizieren. So findet eine inversion of control* ('tschuldigung, aber ich glaube man kann das wirklich nicht auf deutsch sagen) statt und die abhängige Klasse muss die tatsächliche Implementierung der Abhängigkeit definitiv nicht kennen.

*) Das heißt, die Konstruktion der Abhängigkeit erfolgt außerhalb der Klasse.

Klar hat man noch an irgendeiner Stelle die Factory stehen, aber das zu vermeiden wird dann irgendwann auch utopisch - zudem ist die Factory selbst wiederum als Interface oder Generic abstrahierbar und ggf. durch eine andere Implementierung ersetzbar, mockbar oder was auch immer.

Gruß
Finn

Für diesen Beitrag haben gedankt: Martok