Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

11

15.08.2016, 16:23

Ich würde einem Anfänger nie ein Singleton-Pattern für Java vorlegen. Das liegt daran, dass es in Java relativ schwer ist, ein Singleton ordentlich zu implementieren, siehe deine Implementierung. Sobald du mit Threading oder verteilten Anwendungen arbeitest wird das nämlich richtig lustig und man hat dann schnell mehr als eine einzige Instanz...
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

12

15.08.2016, 17:32

Problematisch ist fast immer, dass die Einschränkung "Es darf nur eine Instanz geben" eigentlich gar nicht existiert, sondern entweder ein schlechtes Design drumherum dies "erforderlich" macht, oder nur die Eigenschaft der globalen Zugreifbarkeit der Grund ist, der hinter dem "Es darf nur eine Instanz geben" versteckt wird.


Struktur- und Verhaltensmuster (Observer Pattern, Command Pattern, ...) sind meiner Meinung nach wesentlich wichtiger, als Erzeugungsmuster (Abstract Factory, Prototype, Singleton, ...).
Bedenke aber, dass man für eine gegebene Problemstellung sich ggf. ein passendes Muster sucht. Es ist also gut, sich mit den Mustern auseinanderzusetzen, um sie im richtigen Moment einzusetzen, man sollte aber nicht auf biegen und brechen versuchen, alles was man macht mit Entwurfsmustern umzusetzen. (Darauf wurde ja bereits hingewiesen.)

Weiterhin könnte "Inversion of Control" von Interesse sein. Relevant dabei ist, dass Objekte sich nicht die anderen Objekte bspw. im Global Scope zusammensuchen, die sie benötigen, sondern von außen zugewiesen bekommen. Dies kann per Parameter im Konstruktor, per Setter (Property oder Set-Methode) oder direkter Zuweisung an eine Membervariable passieren.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

13

15.08.2016, 20:48

Jedes Design Pattern wurde als Lösung für ein bestimmtes Problem entworfen, z.B. das Singleton Design Pattern, wenn man nur eine Instanz der Klasse haben will und es keine weitere Instanz der Klasse geben darf.

Diese Aussage würd ich mal etwas relativieren. Es ist nicht so, dass jemand sich ein bestimmtes Problem ausdenkt und dann ein Design Pattern dafür entworfen wird. Eigentlich ist es genau umgekehrt. Ein Design Pattern ist einfach eine bestimmte, abstrakte Struktur, die in gut funktionierenden Lösungen wiederholt identifiziert wurde; im Sinne von: eine Reihe gut funktionierender Lösungen scheinen aus irgendeinem Grund eine bestimmte Struktur gemeinsam zu haben. Jemand hat also mal realisiert, dass in bestimmten Situationen aus irgendeinem Grund am Ende immer eine Lösung einer bestimmten Form am vorteilhaftesten zu sein scheint, dem Ding einen Namen gegeben und ein neues Design Pattern war geboren.

Ganz wesentlicher Aspekt eines Design Pattern ist nicht nur die Lösungsstruktur, sondern immer erstmal die Charakterisierung der Situation, in der das Pattern angebracht ist. Und genau da liegt auch das Problem mit dem seit seiner Geburt missverstandenen Singleton Pattern. Wie so viele andere auch, begehst du den Fehler, "nur eine Instanz der Klasse brauchen" gleichzusetzen mit "nur eine Instanz der Klasse haben dürfen". Der Singleton Pattern macht es zu einer intrinsischen Eigenschaft des Typs (der Klasse), dass zu jedem Zeitpunkt nur eine Instanz existieren kann. Zweck des Singleton Pattern ist es, sicherzustellen, dass von jetzt and bis ans Ende aller Zeit um jeden Preis niemals mehr als exakt eine Instanz von etwas existiert (as in: wenn jemals zwei davon existieren könnten, würde die Welt untergehen). Oder in anderen Worten: Zweck des Singleton Pattern ist es, auszudrücken, dass die Existenz mehr als einer Instanz im fundamentalen Widerspruch zum durch die Klasse modellierten Konzept an sich stehen würde. Als Nebeneffekt des Pattern ergibt sich dann auch ein zentraler, globaler Zugriffspunkt auf die eine Instanz (man beachte, dass es sich dabei nur um einen Nebeneffekt, eine Konsequenz der Anwendung des Pattern und keinesfalls um eine Motivation zum Einsatz oder gar den Zweck des Pattern handelt). Nun ist es aber so, dass man sich in der Realität praktisch niemals in einer Situation findet, wo es tatsächlich darum ginge, sicherzustellen, dass eine Klasse um keinen Preis jemals mehr als einmal instanziert werden kann (ich würde mich in so einem Fall erstmal fragen, was überhaupt der Sinn davon sein sollte, das Ding in eine Klasse zu gießen, wenn es sich um ein dermaßen einzigartiges Gebilde handeln soll). In der Tat hab ich in all meinen Jahren noch kein einziges Beispiel für eine reale Situation gesehen, in der ein Singleton sinnvoll gewesen wäre; selbst rein hypothetische Situationen sind äußerst schwierig sich aus der Nase zu ziehen. Selbst die originale Beschreibung des Pattern hält sich, was das betrifft, eher bedeckt und warnt iirc auch vor den Spätfolgen von potentiellem Patternmissbrauch. Der Novize findet sich dagegen schnell mal in einer Situation, wo erstmal nur eine Instanz einer Klasse gebraucht wird und diese am besten global zugänglich sein sollte, weil ja jeder das Ding zu brauchen scheint. Vom in diesem Falle eigentlich angebrachten Werkzeug der globalen Variable wurden ihm allerdings so viele Schauermärchen erzählt, dass er nach Alternativen sucht, welche er – dank den heutzutage leider quer über das Internet verstreuten Berichten enthusiastischer Halbwissender von der vermeintlichen "Nützlichkeit" eines gewissen "Singleton Pattern" – schnell gefunden zu haben glaubt. Dass das Singleton Pattern in der konkreten Situation nicht nur nicht angebracht ist, sondern auch erst wieder sämtliche Probleme mitbringt, die auch globalen Variablen haben (die Probleme von globalen Variablen resultieren ja nicht aus dem "Variablen", sondern aus dem "global" und – wir erinnern uns – ein Singleton hat als Nebeneffekt ebenfalls einen globalen Zugriffspunkt), merkt der Novize dabei natürlich nicht und geht am besten gleich dazu über, seinen eigenen Blogpost über die vermeintliche "Nützlichkeit" des "Singleton Pattern" zu verfassen, weil das Internet ja noch nicht genug davon hat. Und wenn der Novize Jahre später kein Novize mehr ist, erinnert er sich nichtmehr an diesen einen alten Blogpost, den er heute wohl nichtmehr verfassen würde und denkt nicht daran, diesen wenigstens zu löschen, um anderen Novizen all den Schmerz zu ersparen, den er über die Jahre aufgrund seiner Jugendsünden mit dem Singleton Pattern durchleben musste. Und so schließt sich der ewige Kreislauf von der Infektion eines neuen Wirts mit dem Singleton Antipattern über die Inkubation und Transmission während der ansteckenden Phase bis hin zur, in der Regel leider erst nach langem und schwerem Kampf mit den Symptomen erfolgenden, vollständigen Ausheilung, der in der Regel eine lebenslange Immunisierung folgt...


tl;dr: Wenn du jemals meinst, einen Singleton zu brauchen, liegst du falsch. ;)

Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von »dot« (15.08.2016, 21:20)


14

15.08.2016, 22:12

Problematisch ist fast immer, dass die Einschränkung "Es darf nur eine Instanz geben" eigentlich gar nicht existiert, sondern entweder ein schlechtes Design drumherum dies "erforderlich" macht, oder nur die Eigenschaft der globalen Zugreifbarkeit der Grund ist, der hinter dem "Es darf nur eine Instanz geben" versteckt wird.
Da sehe ich bei den üblichen Singleton-Implementierungen eher die schwierige Testbarkeit und besonders den Krakeneffekt der globalen Zugriffsmöglichkeit als Problem. Der Wunsch nur eine Instanz zu haben, ist meiner Erfahrung nach eher kein Problem und häufig recht sinnvoll.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

15

15.08.2016, 23:57

Ja, aus der globalen Zugreifbarkeit resultieren die ganzen Probleme (keine Austauschbarkeit und somit schlechtere Testbarkeit). Der Wunsch, nur eine Instanz zu haben, ist dadurch gedeckt, nur eine Instanz zu erstellen. Das Single-Pattern hat den Zweck, dies unter allen Umständen Sicherzustellen.
Ich wollte auch nicht auf die Probleme des Patterns selbst hinaus, sondern eher darauf, dass es zu oft benutzt wird, obwohl es nicht notwendig wäre. Würde man es auch nicht verwenden, wenn man andere Möglichkeiten wahrnehmen kann, kommt man nicht zu den Problemen des Patterns.

Und natürlich sind die Standardimplementierung, die grundsätzlich vorgeschlagen werden, nicht gut. Statt eine global zugreifbare, einmalige Instanz zu haben, die keine Schnittstelle implementiert, sollte eben eine Schnittstelle implementiert werden und im Stile von "Inversion of Control" (bspw. per Dependency Injection, sollte man ein solches Framework nutzen) an die Stellen weiterreichen, die dieses brauchen. Diese sollten auch nicht eine Instanz der Singleton-Klasse erwarten, sondern eine Implementierung der Schnittstelle.

Aber ich denke, im weitesten dürften wir wohl die gleiche Meinung haben.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

LInsoDeTeh

Treue Seele

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

16

16.08.2016, 09:12

Ein Beispiel aus der Praxis, um meinen Vorrednern, die sagen, das Singleton Pattern wird oft benutzt, obwohl es nicht gebraucht wird, recht zu geben:

Wenn man ein Dependency Injection Framework wie z.B. Microsoft.Unity verwendet, hat man dort auch Konfigurationsmöglichkeiten, so kann man z.B. pro Konstruktorparameter/Interface festlegen, ob dieser Parameter jedes Mal mit einer neuen Instanz aufgelöst werden soll, oder immer mit der gleichen (Lifetime Manager). Das kann z.B. bei Klassen sinnvoll sein, die einen Datenzugriff bereitstellen. Vor allem bei den "Warmup-Zeiten" von Entity Framework ist es durchaus sinnig, die Klassen nicht immer neu zu erzeugen, sondern eine existierende weiterzuverwenden. Auf diese Weise hat man allein durch die Verwendung von Unity bereits gewährleistet, dass es immer nur eine Instanz einer Klasse gibt, ohne das Singleton Pattern zu verwenden, und diese wird dann in alle Klassen reingereicht, die diese benötigen.

Eine Sache, die ich in der Spieleentwicklung auch ab und zu verwende, sind statische Klassen mit Initialisierungsfunktion. Wenn ich zum Beispiel Button/HUD-Texturen lade, die ich permanent und überall brauche, dann will ich die ja nicht mehrfach laden oder mehrere Instanzen haben, wo die immer gleichen Texturen drin liegen. Also gibt es es eine statische Klasse, die alle häufig verwendeten Texturen enthält und in LoadContent() wird für diese Klasse eine Initialisierungsmethode aufgerufen, die die "Konstruktorlogik" (in Anführungszeichen, denn es gibt ja keinen echten Konstruktor) enthält, also die statischen Eigenschaften mit Content befüllt. Der Nachteil an statischen Klassen ist natürlich, dass sie keine Interfaces implementieren und auch selbst keine Ableitungen haben können. Das ist aber auch in der Regel nicht notwendig, da sie in meinem Anwendungsfall eine Art "strukturierte globale Variable" darstellen. Mehrere Instanzen kann man davon aber auf jeden Fall nicht versehentlich haben :-)

Jar

Treue Seele

Beiträge: 197

Wohnort: Lübeck

Beruf: Softwareentwickler

  • Private Nachricht senden

17

16.08.2016, 09:19

Um nochmal auf dein Texturen speichern Beispiel hinzuweisen
Ich führe das lieber nochmal explizit aus: Ein Singleton ist nicht dafür gedacht, dass man damit Variablen hält, von denen man nur eine haben will. Ein Singleton ist einzusetzen (und nur dann), wenn es unter gar keinen Umständen jemals mehr als eine Instanz davon geben darf. Jede andere Verwendung ist eine Pseudo-Klassen-Gestaltung einer globalen+statischen Variable. Das ist kein Objekt-Orientierungs-Globale-Variablen-Schönmach-Pattern.


Zum Speichern von Texturen gibt es meist einen AssetManager.

LInsoDeTeh

Treue Seele

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

18

16.08.2016, 09:28

Zum Speichern von Texturen gibt es meist einen AssetManager.

"Gibt es" ist ne super Aussage. Wenn man vorgefertigte Engines verwendet, ja. Wenn man es selber macht, muss man diesen auch selber machen.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

19

16.08.2016, 13:43

Da sehe ich bei den üblichen Singleton-Implementierungen eher die schwierige Testbarkeit und besonders den Krakeneffekt der globalen Zugriffsmöglichkeit als Problem. Der Wunsch nur eine Instanz zu haben, ist meiner Erfahrung nach eher kein Problem und häufig recht sinnvoll.

Die typischen Symptome einer Singleton-Infektion sind in der Tat hauptsächlich Folge der globalen Zugriffsmöglichkeit. Das Grundproblem mit dem Singleton Pattern ist aber, dass es ständig in Situationen eingesetzt wird, in denen es einfach von vorn herein völlig unangebracht ist. Das rein prinzipielle Problem mit dem "Wunsch nach nur einer Instanz" ist, dass dieser praktisch nie gegeben sein sollte. Nur weil man im Moment nur eine Instanz von Etwas braucht, heißt noch lange nicht, dass es intrinsische Eigenschaft dieses Etwas zu sein hat, dass es nur einmal instanziert werden kann. Beispiel: Ich brauch in meiner einfachen Grafikanwendung im Moment nur ein Fenster. Also mache ich halt erstmal nur eine Instanz meiner Fenster Klasse. Es wäre absoluter Bullshit, wenn ich diese Fensterklasse nun als Singleton ausführen würde, denn es gibt nichts am Konzept eines Fensters an sich, das sagt, dass es nur ein Fenster geben darf. Insbesondere verbaue ich mir damit alle Wege, wenn ich später einmal vielleicht mehr als ein Fenster bräuchte. Oder: Ich brauche im Moment nur eine Datenbankverbindung. Die Voraussetzungen für einen Singleton sind auch hier nicht gegeben: Es gibt nichts am Konzept einer Datenbankverbindung, das sagt, dass es nur eine davon geben kann. Ich brauche nur einen Renderer. Ich brauche nur einen Drucker. Ich brauche nur eine Tastatur. Ich brauche nur einen Heap. In keiner dieser Situationen ist ein Singleton angebracht. Nochmal: "Ich brauche nur eines" != "Es darf nur eines geben". Singleton ist ausschließlich nur für letzteres gedacht. ausschließlich. Jeder Einsatz des Singleton Pattern weil nur eines von etwas gebraucht wird ist schlicht und einfach falsch. falsch. Und zwar falsch direkt per Definition des Pattern selbst. Falscher geht's also gar nicht mehr...

Eine Sache, die ich in der Spieleentwicklung auch ab und zu verwende, sind statische Klassen mit Initialisierungsfunktion.

Uff, das solltest du dir abgewöhnen. Erstmal: Was genau soll eine "statische Klasse" sein? Ich nehme an eine Klasse, die nur statische Methoden hat!? Wieso muss es dann überhaupt erst eine Klasse sein!? Wieso Initialisierungsfunktion? Es gibt also doch wieder irgendwie sowas wie eine Instanz (im Sinne von: die "gekapselte" Funktionalität ist an einen Satz von Daten gebunden)? Wäre es dann nicht besser, eine explizite Instanz zu haben und deren Konstruktor zur Initialisierung zu verwenden? Wer kümmert sich um die Deinitialisierung? Wie wird sichergestellt, dass 1. korrekt initialisiert wird und 2. auf jede Initialisierung genau eine Deinitialisierung folgt, bevor wieder initialisiert werden kann?

Wenn ich zum Beispiel Button/HUD-Texturen lade, die ich permanent und überall brauche, dann will ich die ja nicht mehrfach laden oder mehrere Instanzen haben, wo die immer gleichen Texturen drin liegen.

Deine Button/HUD-Texturen werden aber weder permanent noch überall gebraucht, zumindest in einem ordentlichen Design. Deine Button/HUD-Texturen werden von jeder Instanz deines UI, das auf einer konkreten Grafikkarte lauft gebraucht und von sonst niemandem und nur so lange es lauft. Und spätestens wenn du einmal mehrere Bildschirme, die an separaten GPUs hängen, ansprechen willst, brauchst du auf einmal doch mehrere Instanzen dieser Button/HUD-Texturen... ;)

Mehrere Instanzen kann man davon aber auf jeden Fall nicht versehentlich haben :-)

Also ich kann mich gerade nicht erinnern, wann ich das letzte Mal versehentlich Instanzen von etwas erzeugt hätte, aber welche Probleme würden in dem Fall denn genau auftreten, dass es das unbedingt zu verhindern gilt!?

LInsoDeTeh

Treue Seele

Beiträge: 372

Wohnort: Essen, Deutschland

Beruf: Team Lead Inhouse-Entwicklung

  • Private Nachricht senden

20

16.08.2016, 14:54

Was genau soll eine "statische Klasse" sein?

Einfach mal "statische Klasse" googlen. ;-) In C# gibt es statische Klassen. Davon erzeugst du selber keine Instanz, das macht die CLR. Die haben sogar auch statische Konstruktoren, allerdings kann man da nicht beeinflussen, wann dieser aufgerufen wird, weil du genau wie bei der GC nicht weißt, wann die intern instanziiert werden. Aus diesem Grund gibt es bei mir zusätzlich eine Methode, die ich unter Kontrolle habe.

Zitat von »dot«

Mehrere Instanzen kann man davon aber auf jeden Fall nicht versehentlich haben :-)
Also ich kann mich gerade nicht erinnern, wann ich das letzte Mal versehentlich Instanzen von etwas erzeugt hätte, aber welche Probleme würden in dem Fall denn genau auftreten, dass es das unbedingt zu verhindern gilt!?

Mir passiert das auch nicht, die Aussage bezog sich auf die Diskussion um das Singleton-Pattern, das ja verhindern soll, dass man "aus Versehen" (Anführungszeichen!) mehrere Instanzen hat... was mit statischen Klassen ja nicht passieren kann.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »LInsoDeTeh« (16.08.2016, 15:00)


Werbeanzeige