mmofacts.com

Verwaltung von Datenbankklasse & co - Alternative zu globalen Variablen?

gepostet vor 16 Jahre, 3 Monate von Fobby

Mal wieder was von mir.

Ich bin gerade dabei, mir ein kleines Framework zu konzipieren und bin gerade in einer Sackgasse angekommen. Dabei bin ich von einem Request-Wrapper ausgegangen, der eine Singleton-Klasse erbt. So weit, so gut. Da kam mit die Datenbank-Klasse in den Sinn und ich habe mich gefragt, ob die auch Singleton-fähig ist. Das Problem taucht auf, wenn mehrere Datenbank-Verbindungen benötigt werden - bei dem Gedanken kamen mir Zweifel an einer Singleton-Datenbank. Aber man findet das so oft als Beispiel ... da muss doch was dran sein ;)

Also als Ausgangssituation, so sah es bei mir bisher aus:

PHP:

$db = new Database(/* Verbindungsdaten */);
function foo() {
global $db;
$db->query("...");
}

 Aber globale Variablen sollte man ja generell vermeiden.

Mein Singleton-Ansatz sieht dann etwa so aus:

PHP:

class Singleton {}
class Database extends Singleton {}
class DatabaseCon1 extends Database {}
class DatabaseCon2 extends Database {}

 Wobei die letzten beiden Klassen nur dazu da sind, die Verbindungsdaten an Database zu übergeben. Aber das find ich recht unelegant, da es kaum wiederverwertbar ist (in einem neuen Projekt muss ich für jede Verbindung wieder neue Klassen anlegen). Außerdem könnte ich so z.B. kein PDO nutzen, da ich nur entweder von Singleton oder von PDO erben kann.

Ein anderer Ansatz ist, häufig benutzte Objekte in ein Array zu packen und das einfach immer referenziert mit zu übergeben. Mir missfällt aber der Gedanke, das immer zu übergeben ...

Noch ein Ansatz ist Registry, siehe http://www.phpit.net/article/using-globals-php/3/ . Aber das ist mir ehrlichgesagt zu viel Schreibarbeit. 2 Zeilen, um an mein DB-Objekt heranzukommen sind mir zu viel.

Wie macht ihr das? Habt ihr da eine elegante Lösung?

gepostet vor 16 Jahre, 3 Monate von Dunedan

Wieso hast du denn eine extra Klasse namens Singleton? Die brauchst du doch gar nicht. Reicht doch wenn du den Konstruktor der DB-Klasse private machst und eine statische Methode anlegst die über Erzeugen eines Objektes entscheidet. Und schon hast du kein Problem mehr mit der Vererbung.

gepostet vor 16 Jahre, 3 Monate von Klaus

Und statt einer speicherst du einfach mehrere Verbindungen. Wenn du die statische Methode normal aufrufst lieferst du die Hauptverbindung zurück und wenn du Parameter übergibst eine andere...

gepostet vor 16 Jahre, 3 Monate von DrakeL

Ich nutze eine Singleton Klasse "SingleConnection", welche die Standarddatenbankverbindung beinhaltet und eine Singleton Klasse "MultipleConnection" die ein assoziatives Array von Datenbankverbindungen besitzt für alle weiteren benötigten Datenbankverbindungen. So hast für beide Fälle ne (in meinen Augen) elegante Lösung. Quellcode der Klassen gibt es gerne per E-Mail.

gepostet vor 16 Jahre, 3 Monate von Fobby

@Dunedan: Ich weiß dass ich die Extra-Klasse nicht brauch aber so wollte ich mir Einheitlichkeit gewährleisten.

@Klaus: Auf die Idee, einfach mehrere Verbindungen zu verwalten bin ich noch nicht gekommen - zu naheliegend ^^ Klingt sinnvoll

@DrakeL: Hm, klingt komplizierter als Klaus' Variante. Ich würd es mir trotzdem gern mal ansehen. Hast gleich eine PM.

Danke an alle für die Antworten :)

gepostet vor 16 Jahre, 3 Monate von n26

Mein Ansatz ist auch so wie der von DrakeL

Und das läuft ja im Prinzip auf folgendes hinaus:

Original von Fobby

Noch ein Ansatz ist Registry, siehe . Aber das ist mir ehrlichgesagt zu viel Schreibarbeit. 2 Zeilen, um an mein DB-Objekt heranzukommen sind mir zu viel.

Wie macht ihr das? Habt ihr da eine elegante Lösung?

Bloss stimme ich dem mit den 2 Zeilen nicht zu ;)

PHP:

$db = Connection::SingleConnection::getInstance()->getConnection();

 Oder eben bei mehreren Verbindungen:

PHP:

$db = Connection::MultipleConnection::getInstance()->getConnection('identifier');

gepostet vor 16 Jahre, 3 Monate von DrakeL

Original von n26

Mein Ansatz ist auch so wie der von DrakeL

Gib zu, du hast es ja auch von mir kopiert, zumindest was das Konzept angeht.

gepostet vor 16 Jahre, 3 Monate von Klaus

viel zu viel Schreibarbeit. global $db; und gut iss. *G*

gepostet vor 16 Jahre, 3 Monate von knalli

Andere Idee:

Warum nicht einfach das getSingleton parametriesieren? Nebst dem Überladen des eigenen Objektes könnte dies auch die notwendige Konfiguration sein.

gepostet vor 16 Jahre, 3 Monate von Kampfhoernchen

was spricht eigentlich dagegen die Datenbankfunktionen einfach statisch zu halten? Dann muss man keine 3000 Objekte mit rumschleppen, sondern macht einfach ein $db::query('SELECT...').

gepostet vor 16 Jahre, 3 Monate von DrakeL

Original von Kampfhoernchen

was spricht eigentlich dagegen die Datenbankfunktionen einfach statisch zu halten? Dann muss man keine 3000 Objekte mit rumschleppen, sondern macht einfach ein $db::query('SELECT...').

Also ich mach lieber eine Methode statisch (Singleton) statt alle Methoden und Attribute.

gepostet vor 16 Jahre, 3 Monate von Amun Ra

Ihr werdet jetzt warscheinlich den Kopf schütteln,
aber ich schreibe einfach:
mysql_query('...');

Funktioniert super...

gepostet vor 16 Jahre, 3 Monate von Fobby

Original von Amun Ra

Ihr werdet jetzt warscheinlich den Kopf schütteln,
aber ich schreibe einfach:
mysql_query('...');

Funktioniert super...

*Beil schärf*

Und was passiert, wenn du zu zwei, drei oder gar 4 Datenbanken Zugriff haben möchtest? Außerdem lass ich ganz gern die Zeit meiner einzelnen Queries loggen, um Performancelücken aufdecken zu können. Da möchte ich dann aber bitte nicht an 954 Stellen die Stoppuhr einbauen müssen ;)

gepostet vor 16 Jahre, 3 Monate von DrakeL

Original von Fobby

Und was passiert, wenn du zu zwei, drei oder gar 4 Datenbanken Zugriff haben möchtest? Außerdem lass ich ganz gern die Zeit meiner einzelnen Queries loggen, um Performancelücken aufdecken zu können. Da möchte ich dann aber bitte nicht an 954 Stellen die Stoppuhr einbauen müssen ;)

Und vor allem die Fehlerüberprüfung hätte man gerne automatisiert mit Logging usw. Oder man würde sich gerne etwas von der Datenbank abkapseln, sodass man später vielleicht mal auf eine andere Datenbank umsteigt. Und vielleicht will man ein Result eleganter auswerten als mysql(i) es anbietet (zum Beispiel hab ich eine Methode die den Inhalt der ersten Zelle zurück gibt und danach bzw. nach dem zeilenweise Durchlaufen der Ergebnisse wird das Result wieder freigegeben).

gepostet vor 16 Jahre, 3 Monate von xou

Ich hab mir für solche sachen wrapper gebastelt, z.B.

PHP:

function q($str) {
    return $GLOBALS['DB']->query($str);
}
?>

selbiges mit e für mysql_real_escape_string, nr für num_rows etc.

Da spar ich mir auch das global $DB; etc :)

Is ganz praktisch wenn man mal schnell was hinhacken will.

gepostet vor 16 Jahre, 3 Monate von Amun Ra

Das ist alles gar kein Problem.
Ich öffne einfach weitere Verbindungen mit neuem Link Identifier.
Also Queries mit PHP loggen / stoppen um Performancelücken aufzudecken, na ja...
Da würd ich mir an deiner Stelle eher mal den MySQL Query Profiler ansehen.

gepostet vor 16 Jahre, 3 Monate von altertoby

Original von Klaus

Und statt einer speicherst du einfach mehrere Verbindungen. Wenn du die statische Methode normal aufrufst lieferst du die Hauptverbindung zurück und wenn du Parameter übergibst eine andere...

Das dürfte dann aber nicht mehr viel mit Singleton zu tun haben, sondern eher ne Factory werden oder?

http://www.dofactory.com/Patterns/PatternFactory.aspx

http://www.dofactory.com/Patterns/PatternSingleton.aspx

Es kann aber auch sein, dass ich einfach in dem ganzen php-Wirrwarr keine Ahnung hab, und gerade völlig daneben liege :) Aber wenn ich  mich nicht irre dürfte zum Verwalten von Datenbankverbindungen so ne Factory gerade richtig sein.

gepostet vor 16 Jahre, 3 Monate von knalli

Designpattern sind nicht PHP-spezifisch.. aber vielleicht habe ich das auch falsch verstanden @vorposter.

Nur eine Factory erstellt (produziert, haha..) Objekte; sie hält keine Objekte vor. Man kann natürlich hingehen und eine Art Cache bzw. Map aufbauen, die die erstellten Objekte "irgendwie" (Definiere..) festhält und abrufbar macht.. dann kann man auch genausogut das Singleton-Pattern nehmen, und parametrisiert obzw. konfigurierbar machen.

Bleistift: Database::getInstance('identification01') != Database::getInstance('identification02') oder

von mir aus auch Database::getInstance('databaseurl1') != Database::getInstance('databaseurl2') oder sogar Database::getInstance('host', 'port', 'user', 'pass', 'db') != Database::getInstance('host', 'port', 'user2', 'pass2', 'db2')

gepostet vor 16 Jahre, 3 Monate von altertoby

Knalli,

es ist mir sehr wohl bewusst dass Designpattern nichts sprach-spezifisches sind. Ich meine mit "php-Wirrwarr", dass ich z.B. nicht genau weiß was globale Variablen bedeuten ect.  Also ob ihr evt. schon indirekt darüber geredet habt, ich nur dank meiner php-Unwissens davon nix mitbekommen habe ;)

Also ich hatte die Factory mir so vorgestellt (pseudo-c#-Code, ich glaube die Entspricht auch nicht 100% dem Pattern, aber herstellen tut sie was :))

C#:

public static class DbFactory
{
private static Dictionary connections;
public static GetDbConnection()
{
if(connections.ContainsKey("Default") == false)
connections.Add("Default", new DbConnection(....));
return connections["Default"]
}
public static GetDbConnection(string name)
{
if(connections.ContainsKey(name) == false)
return null;//Entweder Fehler hier erzeugen, oder ebenfalls neue Db-Verbindung erstellen, oder oder
return connections[name]
}
}

 Gut die Variante Singleton würde ähnlich aussehen nur man braucht sie nicht wirklich oder? Das wäre ja sozusagen nur noch nen zusätzlicher Wrapper oder?

Bei mir sind "nur" die DbConnections im Memory (hauptsächlich), bei dir zusätzlich noch die Singleton-Klasse (auch wenn die so gut wie nix weiter als die DbConnections enthält).

gepostet vor 16 Jahre, 3 Monate von DrakeL

Original von altertoby

[...]
if(connections.ContainsKey("Default") == false)
connections.Add("Default", new DbConnection(....));
[...]

 Gut die Variante Singleton würde ähnlich aussehen nur man braucht sie nicht wirklich oder? Das wäre ja sozusagen nur noch nen zusätzlicher Wrapper oder?

Bei mir sind "nur" die DbConnections im Memory (hauptsächlich), bei dir zusätzlich noch die Singleton-Klasse (auch wenn die so gut wie nix weiter als die DbConnections enthält).

Beim Quellcodebeispiel frage ich mich, mit welchen Parametern würdest du die Connection erzeugen? Hast ja schön mit Punkten ausgelassen... :)

Klar braucht man Singleton nicht. Der einzige Unterschied wäre: Singleton Variante hat eine statische Methode und sonst wird alles über das Objekt gehandhabt und bei dir ist halt alles statisch. Im übrigen würde ich das "GetDbConnection()" etwas anders implementieren:

C#:

public static GetDbConnection()
{
return self::GetDbConnection("Default");
}

Wobei ich da sagen würde, das Erzeugen von neuen Verbindungen, wenn die zu abfragende nicht existiert wird schwer möglich sein, da du keinerlei Daten für die Verbindung hast, also wäre eine Setter Methode nicht schlecht. Dann ist deine Factory allerdings ein reiner statisch globaler Datenhalter für die Verbindungen, also das gleiche wie die Singleton Variante von n26 und mir angedeutet hat.

gepostet vor 16 Jahre, 3 Monate von TheUndeadable

> mysql_query('...');

Ist nur aus Sicherheitsgründen nicht mehr empfohlen...

Das Interface für die puren MySQL-Routinen wird auch nicht mehr so gepflegt, wie die neuen Schnittstellen.

gepostet vor 16 Jahre, 3 Monate von altertoby

Original von DrakeL

Beim Quellcodebeispiel frage ich mich, mit welchen Parametern würdest du die Connection erzeugen? Hast ja schön mit Punkten ausgelassen... :)

Ich dachte, dass es eine Möglichkeit gibt von dem Parameter (name war nur ein von mir gewählter, könnte auch Host oder DbName oder oder sein) auf die nötigen Daten für die Erstellung der Connection zu schließen. Wenn dem nicht so ist, dann muss man wohl oder übel noch nen Setter einbauen.

So langsam verstehe ich auch warum ihr eher zu einem Singleton tendiert.

Also nur nochmal für mich (der Rest der Welt scheints ja wieder verstanden zu haben :) ): Du hast die benötigten Daten (Host, Passwort, Datenbank-Name,...) um eine Verbindung herzustellen. Wenn aber eine Verbindung von eben diesen Daten bereits existiert, soll diese bestehende Verbindung zurückgegeben werden?

Ich dachte es ging mehr um die Verwaltung eines statischen Connection-Pools --> Mein Fehler :)

Original von DrakeL

Im übrigen würde ich das "GetDbConnection()" etwas anders implementieren:

C#:

public static GetDbConnection()
{
return self::GetDbConnection("Default");
}

Ist mir auch eingefallen, jedoch dachte ich, dass die Standard-Verbindung vllt. erzeugt werden kann, während bei den anderen eher ein Fehler geworfen würde. Da war ich dann nur zu faul das "if" in die GetDbConnection(name) einzubauen :) Recht hast du aber in jedem Fall!

gepostet vor 16 Jahre, 3 Monate von DrakeL

Original von altertoby

Also nur nochmal für mich (der Rest der Welt scheints ja wieder verstanden zu haben :) ): Du hast die benötigten Daten (Host, Passwort, Datenbank-Name,...) um eine Verbindung herzustellen. Wenn aber eine Verbindung von eben diesen Daten bereits existiert, soll diese bestehende Verbindung zurückgegeben werden?

Ich dachte es ging mehr um die Verwaltung eines statischen Connection-Pools --> Mein Fehler :)

Da der OP mit PHP angefangen hatte, hab ich in PHP "gedacht", da macht Connection Pooling nicht wirklich viel Sinn in der Regel. Ich habe die benötigten Daten klar, aber davon darf meine Singletonklasse nichts wissen. Die bekommt einfach eine offene Verbindung zugewiesen, entweder in der Einzelform oder in der mehrfachen Form mit eindeutigen Namen.

Beispiel Einzelform:

PHP:

//Connection zuweisen
$connection = new PdoConnection($host, $user, $pass, $db);
SingleConnection::getInstance()->setConnection($connection);
//und später dann abfragen
$connection = SingleConnection::getInstance()->getConnection();

Beispiel Mehrfachform:

PHP:

//Connection zuweisen
$connection = new PdoConnection($host, $user, $pass, $db);
MultipleConnection::getInstance()->setConnection('bugtracker', $connection);
//und später dann abfragen
$connection = MultipleConnection::getInstance()->getConnection('bugtracker');

Klar würde auch mit einer Klasse funktionieren die beide Formen beherrscht und auch ohne Singleton mit rein statischen Methoden, aber hey, ich liebe klare Strukturen. :)

gepostet vor 16 Jahre, 2 Monate von Phoscur

Ich denke das Thema ist relativ abgeschlossen, ich möchte bloß hierzu noch etwas hinzufügen, denn die Idee vereinfach einiges.

Original von Kampfhoernchen

was spricht eigentlich dagegen die Datenbankfunktionen einfach statisch zu halten? Dann muss man keine 3000 Objekte mit rumschleppen, sondern macht einfach ein $db::query('SELECT...').

PHP: von Mattlock http://de2.php.net/manual/de/language.oop5.patterns.php

// Requires PHP 5.3!
class Singleton {
    public static function
Instance() {
        static
$instances = array();
       
$class = get_called_class();
        if (!
array_key_exists($class, $instances)) {
           
$instances[$class] = new $class();
        }
        return
$instances[$class];
    }
    public static function
__callStatic($func, $args) {
       
$class = static::Instance();
        return
call_user_func_array(array($class, '__'.$func), $args);
    }
}

Auch das ließe sich recht einfach um eine weitere Instanz erweitern, die man zB auhc mit einer Methode wie DB::useInstance('dbconnection2') welchseln könnte, ohne groß irgendwelchen anderen Code ändern zu müssen.

Nachteil ist halt dieses PHP 5.3, aber ich denke schon allein wegen dieses Codeschnippsels lohnt es sich für eine Version zu proggen, deren alpha1 seit August draußen ist. Namespaces kommen übrigens auch dazu...

Und nochwas:

Singleton ist in PHP leider teils kompliziert einzubauen, bzw teils nicht ganz unperfekt möglich wenn man eine Klasse erweitern will. Thema Datenbank zum Beispiel mysqli.

Aufgrund einer komischen PHP Eigenschaft kann man einen public Konstruktor nichtmehr protected oder private deklarieren, aber umgekehrt. Das erschwert das einbauen eines Singletons in die mysqli Klasse.

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Phlegma

Und nochwas:

Singleton ist in PHP leider teils kompliziert einzubauen, bzw teils nicht ganz unperfekt möglich wenn man eine Klasse erweitern will. Thema Datenbank zum Beispiel mysqli.

Aufgrund einer komischen PHP Eigenschaft kann man einen public Konstruktor nichtmehr protected oder private deklarieren, aber umgekehrt. Das erschwert das einbauen eines Singletons in die mysqli Klasse.

Ist ja auch gut so, sonst hätte man einen Widerspruch in sich. Gibts überhaupt objektorientierte Sprachen in der das möglich ist? Ableitung heißt schließlich eine Klasse erweitern und nicht einschränken. Eine Unterklasse muss kompatible zur Oberklasse sein. Das wäre sie nicht mehr wenn du eine Methode die public war auf private änderst (auch beim Konstruktor, was der Grund sein mag, dass meine Konstruktoren bei abstrakten Klassen zum Beispiel immer protected sind).

Was deine Singleton Klasse machen soll versteh ich nicht, kannst es aber gerne erklären was du da vor hast. Solltest aber zumindest deren Namen ändern, weil die Klasse nichts mit Singleton zu tun hat.

gepostet vor 16 Jahre, 2 Monate von Phoscur

Ich habe geört bei Java und C ist es genau so, dass man etwas von public in protected deklarieren kann, aber nicht umgekehrt.

Vorab: Ich würde mich zugern damit schmücken, aber das ist nicht mein Code. Siehe Link; unten in den Kommentaren.

Der Schnipsel bewirkt, dass man die Klasse komplett statisch ansprechen kann, obwohl die Methoden gar nicht statisch deklariert sind. Alles läuft durch die __callStatic(..) Methode - ich hoffe du kennst __call(..) bereits - und wird an die unique Instanz im Singleton Pattern weitergeben. Instance() wird auch oft getInstance() genannt.

Beispiel:

Anstatt

PHP:
DB::getInstance()->query($sql);

Kannst du einfach

DB::query($sql);

schreiben, wobei die Methode query() nicht static deklariert ist.

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Phlegma

Ich habe geört bei Java und C ist es genau so, dass man etwas von public in protected deklarieren kann, aber nicht umgekehrt.

[...]

Der Schnipsel bewirkt, dass man die Klasse komplett statisch ansprechen kann, obwohl die Methoden gar nicht statisch deklariert sind. Alles läuft durch die __callStatic(..) Methode - ich hoffe du kennst __call(..) bereits - und wird an die unique Instanz im Singleton Pattern weitergeben. Instance() wird auch oft getInstance() genannt.

In Java kannst du in einer abgeleiteten Klasse die Sichtbarkeit einer Methode aus der Oberklasse niemals einschränkender machen. Daher ist eine Methode in der Oberklasse protected, darf diese in der abgeleiteten Klasse public sein, aber nicht private.

Wegen "__callStatic()": Wieso definierst du nicht einfach alle Methoden statisch, wenn diese statisch aufgerufen werden sollen? Was passiert wenn ich folgendes mache:

PHP:

 DB::bla();

Kommt dann ein Fehler weil keine Methode "bla" existiert? Des weiteren sehe ich Probleme darin die Methoden nach außen hin zu Dokumentieren, sodass die Auto-Vervollständigung funktioniert und auch die Hilfe der API Dokumentation angezeigt wird.

Die Vorteile alles statisch zu machen ok, aber dann würde ich die Methoden eher statisch machen als den Umweg über "__callStatic()" zu gehen. Oder gibt es noch Vorteile die ich gerade nicht sehe?

Und was passiert, wenn ich zwei Verbindungen brauche? Eine zu meiner Datenbank und einer zu einer fremden Datenbank (sei es Bugtracker, Forum oder ähnliches)? Das sehe ich als Hauptargument gegen die statische Lösung an bzw. der Lösung die Klasse für die Verbindung direkt statisch zu machen.

gepostet vor 16 Jahre, 2 Monate von Phoscur

Original von DrakeL

In Java kannst du in einer abgeleiteten Klasse die Sichtbarkeit einer Methode aus der Oberklasse niemals einschränkender machen. Daher ist eine Methode in der Oberklasse protected, darf diese in der abgeleiteten Klasse public sein, aber nicht private.

Gut, dann war ich falsch informiert...

Wegen "__callStatic()": Wieso definierst du nicht einfach alle Methoden statisch, wenn diese statisch aufgerufen werden sollen?

Es ging nur darum eine Singleton-ähnliche Variante vorzugeben. Wie du vllt siehst kannst du hier innerhalb der Klasse so programmieren als wäre es eine ganz normale Klasse mit Instanz. Alle Methoden statisch zu deklarieren hätte einige Nachteile, nicht nur, dass es einfach umständlich wäre, sondern das vorher beschriebene DB::changeConnection($data) wäre nicht möglich. Du kannst die Klasse übrigens auch einfach namentlich ableiten und schon hast du eine zweite Instanz.

Was passiert wenn ich folgendes mache:

PHP:

 DB::bla();

Kommt dann ein Fehler weil keine Methode "bla" existiert?

Ja. Ganz normal. Du kannst in __callStatic() ja noch eine Exception werfen, dann findest du den Fehler schneller.

Des weiteren sehe ich Probleme darin die Methoden nach außen hin zu Dokumentieren, sodass die Auto-Vervollständigung funktioniert und auch die Hilfe der API Dokumentation angezeigt wird.

Daran hab ich auch gedacht. Aber bei __get() und __set() ist das auch nicht möglich.

aber wenn du programmierst kannst du ja auch kürz ein

$DB = DB::getInstance();

voranstellen. Dann schreibst du alles mit $DB->methode() und am Ende lässt du ein Replace drüber laufen... Ach kA. Kannst ja auf beide Arten proggen. Es ging ja auch darum, dass viele ohne Autovervollständigung schreiben. Oder du legst die Klasse kommentiert statisch bei^^

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Phlegma

Es ging nur darum eine Singleton-ähnliche Variante vorzugeben. Wie du vllt siehst kannst du hier innerhalb der Klasse so programmieren als wäre es eine ganz normale Klasse mit Instanz. Alle Methoden statisch zu deklarieren hätte einige Nachteile, nicht nur, dass es einfach umständlich wäre, sondern das vorher beschriebene DB::changeConnection($data) wäre nicht möglich. Du kannst die Klasse übrigens auch einfach namentlich ableiten und schon hast du eine zweite Instanz.

Von einer Klasse ableiten nur um an eine zweite Instanz zu kommen, weil die eigentliche Klasse eine Einschränkung macht ist in meinen Augen ein Design Fehler...

Warum wäre "DB::changeConnection($data)" nicht möglich? Bei einer rein statischen Klasse müssten halt auch alle Attribute statisch sein, falls du auf das Problem mit den Attributen hinweisen wolltest. :)

Daran hab ich auch gedacht. Aber bei __get() und __set() ist das auch nicht möglich.

Einer von mehreren Gründen, warum ich keine Interzeptormethoden nutze mit Ausnahme der "__toString()" Methode bei eigenen Fehlerklassen... spätestens seit dem ich verzweifelt mehrere Stunden nach einem Fehler suchte, weil die Interzeptormethode nicht aufgelöst wurde. :)

aber wenn du programmierst kannst du ja auch kürz ein

$DB = DB::getInstance();

voranstellen. Dann schreibst du alles mit $DB->methode() und am Ende lässt du ein Replace drüber laufen... Ach kA. Kannst ja auf beide Arten proggen. Es ging ja auch darum, dass viele ohne Autovervollständigung schreiben. Oder du legst die Klasse kommentiert statisch bei^^

Bei mir ist es grundsätzlich so (da ich recht viele Singletonklassen besitze, dafür keinerlei globalen Variablen verwenden würde):

  • Wird das Objekt der Klasse innerhalb einer Methode nur einmal benötigt, wird dies per "Klasse::getInstance()->method();" gemacht
  • Wird das Objekt der Klasse innerhalb einer Methode mehrmals benötigt, wird es immer in eine lokale Variable zwischengespeichert, daher vorher "$object = Klasse::getInstance();"

Das empfand ich bisher als die für mich optimale Lösung.

Auto-Vervollständigung empfinde ich bei einer größeren API als ein sehr angenehmes Feature und im Zusammenhang mit der Anzeige der API Dokumentation auch recht zeitsparend. Wenn man nur wenige Klassen hat ist das natürlich weniger relevant.

gepostet vor 16 Jahre, 2 Monate von Phoscur

Original von DrakeL

Original von Phlegma

Du kannst die Klasse übrigens auch einfach namentlich ableiten und schon hast du eine zweite Instanz.

Von einer Klasse ableiten nur um an eine zweite Instanz zu kommen, weil die eigentliche Klasse eine Einschränkung macht ist in meinen Augen ein Design Fehler...

Ich meine ein einfaches class DB2 extends DB {} und möglicherweise noch das hinzufügen der Logindaten, wie auch immer du das regelst. Das ist kein Designfehler, dieser Singleton erlaubt verschiedene Instanzen bei versch. vererbeten Klassen.

Warum wäre "DB::changeConnection($data)" nicht möglich? Bei einer rein statischen Klasse müssten halt auch alle Attribute statisch sein, falls du auf das Problem mit den Attributen hinweisen wolltest. :)

Das war quatsch, nicht richtig nachgedacht..

Daran hab ich auch gedacht. Aber bei __get() und __set() ist das auch nicht möglich.

Einer von mehreren Gründen, warum ich keine Interzeptormethoden nutze mit Ausnahme der "__toString()" Methode bei eigenen Fehlerklassen... spätestens seit dem ich verzweifelt mehrere Stunden nach einem Fehler suchte, weil die Interzeptormethode nicht aufgelöst wurde. :)

Ich finde das eigentlich ganz hübsch, wenn man die normale Sprachgrammtik für die Funktionsweise eines Objekts benutzen kann. Auch die Interfaces Iterator und ArraysAcces sind hübsch.

Bei mir ist es grundsätzlich so (da ich recht viele Singletonklassen besitze, dafür keinerlei globalen Variablen verwenden würde):

  • Wird das Objekt der Klasse innerhalb einer Methode nur einmal benötigt, wird dies per "Klasse::getInstance()->method();" gemacht
  • Wird das Objekt der Klasse innerhalb einer Methode mehrmals benötigt, wird es immer in eine lokale Variable zwischengespeichert, daher vorher "$object = Klasse::getInstance();"

Das empfand ich bisher als die für mich optimale Lösung.

Auto-Vervollständigung empfinde ich bei einer größeren API als ein sehr angenehmes Feature und im Zusammenhang mit der Anzeige der API Dokumentation auch recht zeitsparend. Wenn man nur wenige Klassen hat ist das natürlich weniger relevant.

aus Klasse::getInstance()->methode() fällt halt das "getInstance()->" weg...

Vllt findet sich ja noch etwas für die Autovervollständigung, dann wäre das Konstrukt doch sehr gut oder?

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Phlegma

Ich meine ein einfaches class DB2 extends DB {} und möglicherweise noch das hinzufügen der Logindaten, wie auch immer du das regelst. Das ist kein Designfehler, dieser Singleton erlaubt verschiedene Instanzen bei versch. vererbeten Klassen.

DB ist doch eine Implementierung für eine bestimmte Datenbank, oder?

In dem Falle wäre es von der Namensnennung bei deiner Verwendung in etwa so:

PHP:

class DB2 {}
class Bugtracker extends DB2 {}
class Forum extends DB2 {}

Das heißt für jede Datenbank eine neue abgeleitete Klasse? Finde ich sehr ungewohnt. Dann doch lieber normale Connectionklassen und globalen Zugriff über Singleton Containerklassen (siehe ersten Kommentar). :)

Ich finde das eigentlich ganz hübsch, wenn man die normale Sprachgrammtik für die Funktionsweise eines Objekts benutzen kann. Auch die Interfaces Iterator und ArraysAcces sind hübsch.

Ich fand diese Methoden auch hübsch, allerdings auch recht Fehleranfällig, da diese nur beim Zugriff von außen aufgelöst werden. Wenn du eine Singleton Klasse hast aus der du eine Methode eines Objektes aufrufst und versuchst innerhalb der aufgerufenen Methode das Objekt der Singleton Klasse auf diese Weise zu nutzen, werden die Interzeptormethoden nicht aufgelöst (in etwa so war das Konstrukt bei mir)... Ich habe etliche Stunden verbracht um die Ursache zu finden und bin danach lieber weg von diesen Methoden hin zu eindeutigen Methodenaufrufen. Da finde ich die Umsetzung in C# wesentlich geschickter.

Die Interfaces der SPL klar, da gebe ich dir Recht, dass diese recht nützlich sind.

aus Klasse::getInstance()->methode() fällt halt das "getInstance()->" weg...

Vllt findet sich ja noch etwas für die Autovervollständigung, dann wäre das Konstrukt doch sehr gut oder?

Also bei Eclipse PDT geht das dann schon mal nicht. Allein schon nicht, weil es kein PHP 5.3 kann bisher. :) Gut ist da natürlich relativ, ich finde ein Singleton einfach übersichtlicher und Schreibfaul bin ich ja nicht. Das dürfte aber dann jetzt eher Richtung Geschmackssache gehen.

gepostet vor 16 Jahre, 2 Monate von KoMtuR

Also ich weiß ja nicht was dieses Kontrukt soll, aber es hat einfach mehr Nachteile als Vorteile.  Wenn es überhaupt Vorteile haben sollte, dann das man scheiß Code schreiben kann.

Ein Singleton zeichnet sich halt dadurch aus, dass es eine statische Funktion zum beziehen des Singletons hat und die Restlichen nicht umsonst normal definiert sind.

Du machst eine Erweiterbarkeit zwar vielleicht möglich, aber es verwirrt den nächsten Programmierer und verleitet ihn so ein Mist weiter fortzuführen. Diese komischen, wie nennt sie DrakeL nochmal, Interzeptormethoden verleiten nur dumme Konstrukte zu schrieben, weil man zu Faul ist alles einzeln hinzuschreiben. Mir kommts so vor, als würden die Entwickler von PHP zuviel scheiss bei Perl abschauen und versuchen eine automatische Obfuskation des Codes zu erzwingen.

Kommen wir mal zu den Nachteilen:

1. Wenn du mir zb. die Klassenbeschreibung (also nur die vorhandenen Funktionen) vorlegst, dann sehe ich darin eine statische Klasse. Gut dann würde ich auch so damit arbeiten. Die Klasse heißt aber Singleton. Nun könnte man auch denken: Ok das ding heißt Singleton also benutze ich es auch so, weil ich ja weiß, wie sie so eine Klassenstruktur aufgebaut ist. Das kann ich zwar machen, indem ich Instance() aufrufe und dein static-call gedöhns da einfach übersehe, aber dann haste 2 Codekulturen im Projekt. Macht das weitere Arbeiten an dem Projekt zur Qual.

2. Wird bei jeder Funktion, die du da aufrufst genau das aufgerufen:

  1. statische Funktion Instance
  2. Überprüfung welche Instanz denn nun zurückgeben muss bezogen auf den Klassennamen
  3. Rückgabe der Instanz
  4. Aufruf der Funktion

So das sind für JEDEN Aufruf einer Funktion 4 Teile. Wobei das insgesamt 4 Funktionsaufrufe sind, eine Überprüfung und ein erstellen eines Arrays. (Wobei mich ein Benchmark zu der Funktion call_user_func_array zum normalen Funktionsaufruf mal interessieren würde). Achja und eine Zeichenkettenverknüpfung

Ich wills nochmal wiederholen: FÜR JEDEN AUFRUF

Richtiges Singleton-Pattern:

Kleiner Overhead am Anfang, weil man mit getInstance() die aktuelle Instanz holen muss. Maximal eine Überprüfung und gegebenfalls eine Erstellung von der Instanz.
Danach kommen die Funktionsaufrufe, die von php direkt übersetzt werden können. Die call_user_bla muss doch sicherlich erst nen Aufurf starten, obs die Klasse und so gibt. In welchem Scope läuft das dann eigentlich. Kann ich mit der Funktion dann auch protected oder gar private Funktionen aufrufen? Oder gehen da nur public Funktionen.

Geschweige denn die Sache mit dem Aufrufen irgendwelcher Funktionen, die es nicht gibt. Dann kommt ne schöne Fehlermeldung innerhalb der Klasse, aber nicht dort, wo der Fehler begangen wurde. Super Grundlage, um Fehlerbehebung zu betreiben.

Ich rate dir eins: Geh in dein Explorer und lösch die Dateien und mach die ordentlich. So kommt nur Scheisse bei raus oder mach sie gleich komplett statisch. Diese Interzeptormethoden sind nur sinnloses Spielzeug, was zu verdammt inkonsistenten Code führt und einfach mehr Probleme (lassen wir mal die Unübersichtlichkeit weg, die viel mehr eine Rolle spielen wird) als sie Vorteile schafft. Und man sollte nicht, nur weil man schreibfaul ist, auf so eine Dreckslösung zurückgreifen.

Und was ist wenn ich diese Singleton-Klasse ableite? Ich muss mich an Namensregeln halten. Man muss also immer ein __ davor machen. Sicherlich kann man den Code diesbezüglich in der Singleton-Klasse abändern, aber das hat keine Hand und kein Fuß.

Sorry das ich nun etwas ausfallender geworden bin, aber bei solchem Code könnte man kotzen, vorallem weil der wegen der Bequemlichkeit, die dadurch entsteht, nur zu scheiß Code führt.

edit:

Ich meine ein einfaches class DB2 extends DB {} und möglicherweise noch das hinzufügen der Logindaten, wie auch immer du das regelst. Das ist kein Designfehler, dieser Singleton erlaubt verschiedene Instanzen bei versch. vererbeten Klassen.

Also erbst du auch alle möglichen Funktionen von der Klasse DB, die du a) vielleicht nicht brauchst und b) vielleicht auch garnicht in dieser DB-Variante funktionieren. Na prima

edit2:

Wenn ich mich in Rage rede vergess ich immer was zu schreiben. Was passiert, wenn die Anzahl der parameter nicht stimmt? Wird sie trotzdem aufgerufen, oder wird sie dann nicht gefunden?

gepostet vor 16 Jahre, 2 Monate von Phoscur

Okay. Du magst das Kontrukt nicht.

Interessant eine Opposition zu hören. Zu den Fehlern bzw der Fehleranfälligkeit:

Wenn du innerhalb der __callStatic() Methode überprüfst und ggf Exceptions wirfst, dann kann man den Fehler genauso leicht, manchmal sogar noch leichter finden, weil die Trace nunmal besser ist als PHPs Standartfehlermeldungen. Und natürlich hat man damit keinen protected oder gar private Access, das wärs ja noch!

Ist auch egal, ich hab das noch nicht implementiert, einfach weil ich noch kein PHP 5.3 habe. Zudem hab' ich schon ein anderes Problem mit dem Singleton in der Datenbanklasse (siehe weiter oben Kontruktor protected deklarieren).

Hier nun wie ich das gelöst habe:

PHP:

class DB extends mysqli
{
/**
* Database Contruktor
* Returns the unique Database instance
* @return DB
*/
public static function getInstance()
{
if (self::$Instance === NULL)
{
self::$singletonacc = true;
self::$Instance = new self();
}
return self::$Instance;
}
protected static $singletonacc = false; # needed due to __construct problem..
private final function __clone() {}
/**
* PROTECTED!
*/
public function __construct() # due to some PHP "bug" I can't declare the method protected/private
{
if (!self::$singletonacc)
{
throw new DBException('Use ::getInstance() to get the DB object!');
}
$cnfg = new DB_config($this);
@parent::__construct($cnfg->host,$cnfg->user,$cnfg->passwd,$cnfg->dbname);
if (mysqli_connect_error())
{
throw new DBException('Connection failed ['.mysqli_connect_errno().']:'.mysqli_connect_error());
}
self::$singletonacc = false;
}
// ...
}

 Achja die Klammern... das is Stilsache, ich überlege grad ob ich mich wieder umgewöhne und die Anfangsklammern hinter die jeweilige Funktion oder Bedingung schreibe...

Sorry das ich nun etwas ausfallender geworden bin, aber bei solchem Code könnte man kotzen, vorallem weil der wegen der Bequemlichkeit, die dadurch entsteht, nur zu scheiß Code führt.

Naja. Ist es nicht die Bequemlichkeit, die den Programmierer zur Wiederverwendbarkeit motiviert? Ist es nicht die Faulheit, die ihn dazu bringt es schon beim ersten Mal so gut wie möglich zu machen, damit er danach weniger Arbeit hat?

Aber das ist wohl ein anderes Thema, sollte nur rhetorisch sein.

gepostet vor 16 Jahre, 2 Monate von KoMtuR

Eine Alternative:

PHP:

Class DB {
private static $instance = null;
private $mysqli;
public static function getInstance() {
if(self::$instance == null) {
self::$instance = new self();
self::$instance->mysqli = self::loadMysqli();
}
}
//Vermittlung für neue Instanzen
public static function newInstance($dbcfg = null) {
if($dbcfg == null)
$dbcfg = new DB_config($this); //was immer das für einen Sinn haben soll ;)
$newInstance = new DB();
$newInstance->mysqli = DB::loadMysqli($dbcfg);
}
//wegen Singleton protected
protected function __contruct() {
}
// das statisch könnte man auch weglassen, aber es wird ja nicht unbedingt in der Klasse selber gebraucht
// könnte man auch in den Kontruktor packen, nur weiß ich nicht ob der auch Defaultwerte zulässt
protected static function loadMysqli($dbcfg = null) {
if($dbcfg == null)
$dbcfg = /* nutze hier die Standardparameter */;
$this->mysqli = new mysqli($dbcfg->host,$dbcfg->user,$dbcfg->passwd,$dbcfg->dbname);
}
/*
Hier nun alle Funktionen anbieten, die deine DB-Klasse vom Mysqli nutzen soll.
Ich bring dir hier mal ein Beispiel. Ist alles eignetlich recht schnell geschrieben
*/
public function query($query, $resultmode = MYSQLI_STORE_RESULT) {
return $this->mysqli->query($query, $resultmode);
}
}

Ich hoffe mal einfach, dass PHP das ganze beherrscht. Also einen geschützten Konstruktor innerhalb einer Klasse erlauben.

Neue Instanzen bekommste durch newInstance, wo du die eigene Konfiguration anbieten kannst, oder halt das komische Konstrukt von dir verwendet wird (wo mir der Sinn noch nicht wirklich schlüssig ist).

Der einzige Nachteil darin ist nun, dass du alle Funktionen, die du vom mysqli brauchst, in deiner Klasse einfügen musst.

Wenn du innerhalb der __callStatic() Methode überprüfst und ggf Exceptions wirfst, dann kann man den Fehler genauso leicht, manchmal sogar noch leichter finden, weil die Trace nunmal besser ist als PHPs Standartfehlermeldungen. Und natürlich hat man damit keinen protected oder gar private Access, das wärs ja noch!

Noch mehr Überprüfungen pro Funktionsaufruf. Dann wird dieser Mist sicher noch schneller Naja bei php hätt ich mir auch was anderes vorstellen können. Die Funktion ist trotzdem rotz. Ich würde es nicht verwenden. Mach doch mal bitte einen Benchmark, ich hab kein php drauf:

PHP:

Class Test {
public static function testIt() {
return 1+1;
}
}
$loops = 10000;
$timeStart = microtime(true);
for($i = $loops; $i > 0; --$i)
Test::testIt();
echo "Verbrauchte Zeit direkter Aufruf: ". (mircotime(true) - $timeStart) ." ms\n";
$timeStart = microtime(true);
for($i = $loops; $i > 0; --$i)
call_user_func(array('Test','testIt');
echo "Verbrauchte Zeit call_user_func: ". (mircotime(true) - $timeStart) ." ms\n";
gepostet vor 16 Jahre, 2 Monate von Phoscur

Weißt du, meine DB Klasse ist noch etwas größer... und ich hatte das weiterreichen an eine mysqli Instanz schon erwogen.

Dieses Schema werde ich sicher nicht umsetzen, zumal ich nicht mehrere Verbindungen brauche.

Du willst mir doch nicht etwa verklickern, dass dein Durchreichen schneller als mein Durchreichen ist?

Nein, ich benutze einen einfachen, wenn auch etwas gepfuschten Singleton.

Das Problem für mehrere Verbindungen und Singleton wurde nun auch ausführlich besprochen, ich habe nichts mehr hinzuzufügen.

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Phlegma

Nein, ich benutze einen einfachen, wenn auch etwas gepfuschten Singleton.

Wieso willst du es unbedingt Singleton nennen, wo es doch keines ist?

Der Rest bleibt wiederum dir überlassen. Da ich mich nicht auf eine DB beschränken will, sondern einen DB Layer obendrüber lege stellt sich bei mir die Frage nach Vererbung gar nicht...

Ich denke schon, dass das weiterreichen schneller ist als dein Konstrukt, da du wesentlich mehr machst.

gepostet vor 16 Jahre, 2 Monate von KoMtuR

Original von Phlegma

Weißt du, meine DB Klasse ist noch etwas größer... und ich hatte das weiterreichen an eine mysqli Instanz schon erwogen.

Dieses Schema werde ich sicher nicht umsetzen, zumal ich nicht mehrere Verbindungen brauche.

Ja mir schon klar das sie größer ist. Schließlich sind da 3 Punkte am Ende Und ich hab das Konstrukt da oben dir nur mal gepostet, weil dieser das Problem mit den öffentlichen Konstruktor von mysqli umgeht und du so dein Singleton und den Vergleich im Konstruktor nicht brauchst. Müsstest aber sicherlich mehr schreiben, wenn du die Grundfunktionen von mysqli nun alle nochmal in die Klasse schreben müsstest.

Du willst mir doch nicht etwa verklickern, dass dein Durchreichen schneller als mein Durchreichen ist?

Hab ich nie behauptet. Das mitn Performancetest war auf dieses komische Singleton-Konstrukt von da oben bezogen. Also nicht deine DB-Klasse, die du hingeschrieben hattest.

Nein, ich benutze einen einfachen, wenn auch etwas gepfuschten Singleton.

Das Problem für mehrere Verbindungen und Singleton wurde nun auch ausführlich besprochen, ich habe nichts mehr hinzuzufügen.

Achso war aus deinem Code nicht ersichtlich, dass du keine weiteren Verbindungen willst. Das
PHP:

		$cnfg = new DB_config($this);
@parent::__construct($cnfg->host,$cnfg->user,$cnfg->passwd,$cnfg->dbname);

 sah ganz danach aus :)

gepostet vor 16 Jahre, 2 Monate von Amun Ra

Mal eine Frage am Rande...
Eine Definition von Singleton:
The singleton pattern:
Some application resources are exclusive in that there is one and only one of this type of resource.
For example, the connection to a database through the database handle is exclusive.
You want to share the database handle in an application because it's an overhead to keep opening
and closing connections, particularly during a single page fetch.
The singleton pattern covers this need.
An object is a singleton if the application can include one and only one of that object at a time.
Wenn ich nun im Script ein mal exklusiv eine "normale" Klasse
$db = new DB('host', 'user', 'pass', 'name');
instanziere und dann nur mit
$db->whatever();
arbeite hab ich doch auch nur ein exklusiven Zugriff auf die DB
oder hab ich was falsch verstanden?
> $cnfg = new DB_config($this);
Oje die Spatzen...
Edit: Der WYSIWYG Editor hier suckt gewaltig!

gepostet vor 16 Jahre, 2 Monate von knalli

Wenn ich nun im Script ein mal exklusiv eine "normale" Klasse
$db = new DB('host', 'user', 'pass', 'name');
instanziere und dann nur mit
$db->whatever();
arbeite hab ich doch auch nur ein exklusiven Zugriff auf die DB
oder hab ich was falsch verstanden?

Im Prinzip schon - bei dir. Allerdings hindert es weder dich noch einen Mitarbeiter von dir (so dahingestellt), einfach nochmal $db = new DB(..) auszuführen. Das ist möglich, wird funktionieren und wahrscheinlich sogar noch nicht mal auffallen (erst später, das sei versichert). Das Beispiel mit der Datenbank ist deswegen gewählt, weil dort in der Regel (also immer :)) eine Verbindung aufgebaut werden muss (pro Objekt). Natürlich will man diese Arbeit auf das notwendigste reduzieren, sprich die Ressource limitieren, reduzieren, exklusiv!

Irgendwie baut ihr da komische Sachen.. da wird das Prinzip Singleton genommen, und dermaßen durch irgendwelche Prozesse gejagt, dass es kaum noch Singleton zu schimpfen ist. Das  Nutzen von sprachtypischen Feinheiten unterstützt zwar den Gedanken des Musters, ist in meinen Augen aber weder wartbar noch nachvollziehbar (Anwender muss es wissen, Compiler/Syntax-Autovervollständigung sowieso).

Und ja, Delegation ist auch ein Software-Prinzip, scheinen einige vergessen zu haben.

gepostet vor 16 Jahre, 2 Monate von Hisoka

Frage mich da echt was ihr unter Singeltones versteht. Oben werden ja wieder unnötige Instancen der Klasse erstellt.

Singletones nutzt man doch eher so:

PHP:

DB::connect('host', 'user', 'pass', 'name');
DB::query('select ...');
//oder
DB::getInstance()->connect('host', 'user', 'pass', 'name');
DB::getInstance()->query('select ...');
//oder bei mehreren DBs
DB::getInstance(1)->connect('host', 'user', 'pass', 'name');
DB::getInstance(1)->query('select ...');

 (Das ist ein Beispiel un muss in der Realität nicht exakt so umgesetzt sein)

Der Vorteil ist nun mal das man nicht immer wieder eine instance von DB übergeben muss oder mit globalen Variablen rumhantieren muss. Dadurch verhindert man einfach ein Chaos an instancen, welche dann auch gerne unnötig viel Speicher fressen.

Edit: lol, das ganze wurde oben schonmal gezeigt. :( Warum das jetz wieder zu Problemen führt ist mal echt fraglich. (Der beitrag bezog sich auf den von Amun Ra)

gepostet vor 16 Jahre, 2 Monate von DrakeL

Original von Hisoka

Frage mich da echt was ihr unter Singeltones versteht. Oben werden ja wieder unnötige Instancen der Klasse erstellt.

Ein Singleton ist eine Klasse von der es nur ein Objekt geben kann. Dein erstes Beispiel nutzt gar kein Objekt, sondern nur statische Klassenmethoden -> kein Singleton. Dein drittes Beispiel soll dir verschiedene Connectionobjekte je nach Parameter zurück geben? Das heißt es gibt mehrere Objekte von der Klasse -> auch kein Singleton. Ich weiss ja nicht, was daran schwer zu verstehen ist an dem Definitionssatz...

Also Singleton = Klasse von der nur ein Objekt existieren kann, dies wird erreich durch:

  • Geschützter Konstruktor
  • Geschützte statische Variable für die einzige Referenz auf das Objekt
  • Öffentliche statische Methode, die beim ersten Zugriff das einmalige Objekt instanziert und in der statischen Variable speichert und bei jedem weiteren Zugriff dieses zurück gibt

Der Vorteil liegt dann daran, dass es nur ein Objekt geben kann auf welches man überall zugreifen kann.

Auf diese Diskussion antworten