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

Thoran

Alter Hase

  • »Thoran« ist der Autor dieses Themas

Beiträge: 520

Wohnort: Stuttgart

Beruf: Senior Software Engineer

  • Private Nachricht senden

1

21.08.2012, 16:23

Engine-Objekt als Singleton?

Hi

weil ich grad in nem anderen Thread hier über Singletons gelesen habe und ich seit geraumer Zeit meine Engine umdesignen will, um den Singeltongebrauch zu reduzieren, mal die generelle Frage was ihr so davon haltet ein Singleton-Engineobjekt zu verwenden. Aufgaben sind im Prinzip Bereitstellung von Accesormethoden zu verschiedenen Managerinstanzen der Engine(Renderer, Audio, Scripting), sowie Applikationspfad und andere Anwendungsdaten zurückgeben.
Ich denke das es vom Prinzip her vertretbar ist zu sagen, es kann pro Appliaktion nur eine Engine geben, andererseits ist mir bewußt, dass ich das Engine-Singletong teilweise wie eine globale Variable verwende.
Was wären Alternativen zum Singleton der Engine-Klasse, so dass sie mir als Provider für die ganzen Manager zu Verfügung steht?

Thoran
Mein Entwicklertagebuch
Aktuelles Projekt: Universum Espionage
Eingestellt:Spieleengine SilverCore
Organisator "Spieleentwickler Stammtisch Stuttgart"

2

21.08.2012, 17:05

Mal sehen: Der Vorteil von Singletons ist, dass du nicht aus versehen ein zweites Objekt anlegen kannst, welches du dann statt dem Original benutzt. Explizit kriegt man das nur mutwillig hin, also reicht es prinzipiell, das Ding unkopierbar zu machen. Weil wohl niemand auf die Idee kommen wird, ein Rendererobjekt lokal zu erstellen, weil er gerade nicht weiß, wie er auf das richtige Rendererobjekt zugreifen soll, ist der Nutzen von Singletons sehr gering, aber diesen einen Fehler fängt es dann doch ab.

Ansonsten ist es wie ein Globales Objekt. Der Nachteil von Globalen Sachen ist, dass man sie von überall manipulieren kann. So erhöhst du die Abhängigkeiten in deinem Code, und je verstrickter der ist, desto wahrscheinlicher sind Fehler. Außerdem sind globale Objekte bei mehreren Threads manchmal ein Problem, allerdings würden die hier angesprochenen Objekte eh über die Threads verteilt genutzt, da ist es auch egal.
Und ob du jetzt ein globales Objekt hast oder Zeiger über 17 Ebenen durchreichst macht letztendlich keinen Unterschied, außer dass du bei letzterem mehr Code schreiben musst (was die Übersichtlichkeit verhindert und somit schlecht ist). Zeiger durchreichen macht dann sind, wenn es verschiedene Rendererobjekte geben kann, und mal die eine oder mal die andere benutzt werden soll. Wenn du schon an Singletons überlegst, trifft das also sicherlich nicht zu.

Man kann aber ja eigentlich auch von "fast globalen" Objekten sprechen. Zum Beispiel die Skriptverwaltung oder der Renderer könnte levelspezifisch sein (und jeweils alle Objekte/Skripte dieses Levels verwalten). Das würde bei globalen Objekten dann Probleme machen, sobald ein Leveleditor mehrere Levels gleichzeitig editieren können soll. Die Lösung wäre diese Objekte irgendwie in einer Levelklasse zu speichern, auf die dann wieder jedes Objekt im Level einen Zeiger hat, um den korrekten Skriptmanager zu benutzen. Das kann bei vielen Verwaltungsklassen dann natürlich sehr nützlich/wichtig sein.
Lieber dumm fragen, als dumm bleiben!

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

3

21.08.2012, 18:10

Ich würde sämtliche Singletons vernichten. Falls Du nämlich wirklich mal mehrere parallele "Engines" brauchst (Editor, multiple Fenster, whatever), dann wäre das mit Singletons nicht möglich.
Zu Accessor-Methoden neigt man immer man wieder, weil man eben doch einen ganzen Haufen von Managern oder sowas hat und die nicht alle einzeln übergeben will. Würde ich aber von abraten. Dann schreib lieber einen Ober-Manager, der alle anderen verwaltet und übergib diesen an die Stellen, die ihn brauchen. So kannst Du jederzeit beliebig viele haben.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »BlueCobold« (21.08.2012, 18:53)


dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

21.08.2012, 18:18

Du hast also den klassischen Fall von Singletons, die eigentlich keine Singletons sein sollten, sondern globale Variablen. Die bessere Lösung wäre Dependency Injection oder auf gut Deutsch: Wenn ein Objekt ein anderes kennen muss, gibt man ihm eine Referenz drauf...

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (21.08.2012, 18:28)


Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

5

21.08.2012, 18:46

Oder Du legst halt eine globale Instanz der Engine an. Die potentiellen Probleme damit wurden ja schon beschrieben, aber es gibt auch Vorteile einer global erreichbaren Instanz: Du kommst jederzeit leicht ran, was anfangs einige Arbeit erspart und schneller zu Ergebnissen führt.

Ein Singleton dagegen ist meiner Meinung nach einfach nur unnütz. Das "Pattern" könnte von heute auf morgen verschwinden und niemand würde darunter leiden.

Bei Splatter habe ich übrigens den Renderer in eine eigene Klasse gepackt und in der Tat bald mehrere Instanzen davon gebraucht. Z.b., um im Multiplayer-Menü mal schnell eine kleine Welt zum Ausprobieren der Steuerung in die GUI zu rendern. Oops... doch nicht so single wie gedacht. Die großen Texturen und sowas stecken allerdings in einem Manager, der in der Tat als globale Instanz rumliegt. Allerdings spricht ja auch nix dagegen, sich für spezielle Zwecke einen zweiten ResourceManager aufzumachen, der seine Aufgabe erledigt und danach wieder sauber abgebaut wird. Man kann sich mit einem Singleton wirklich sehr schön die Zukunft verbauen, und hat außer verletztem Stolz eigentlich nie Nachteile, wenn man ihn wieder ausbaut.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

21.08.2012, 22:29

[...] aber es gibt auch Vorteile einer global erreichbaren Instanz: Du kommst jederzeit leicht ran, was anfangs einige Arbeit erspart und schneller zu Ergebnissen führt.

Das ist imo kein Vorteil, sondern gerade das fundamentale Problem von globalen Variablen. Denn anstatt sich Gedanken über Design zu machen, wirft man von da an mit Abhängigkeiten nurmehr so um sich, meist ohne dass es einem überhaupt auffällt. Globale Variablen sind ein Seitenkanal, durch den Abhängigkeiten abseits aller Schnittstellen in ganzen Source metastasieren können und meistens auch tun. Und wenn man es dann endlich merkt, ist es oft schon zu spät...

Thoran

Alter Hase

  • »Thoran« ist der Autor dieses Themas

Beiträge: 520

Wohnort: Stuttgart

Beruf: Senior Software Engineer

  • Private Nachricht senden

7

22.08.2012, 09:04

Vielen Dank mal für Eure Antworten. Da hier zum Teil auf die Manager eingegangen wurde, wollte ich nochmal klarstellen, dass ich bereits meine ganzen Manager, z.B fürs Rendern von Singleton auf nicht-Singleton umstelle (Wobei mein Renderer ja auf Ogre basiert, was ja bekanntlich mit Singletons nur so um sich wirft). Die einzige Frage die bleibt und darauf habe ich ja bereits ein paar Antworten bekommen ist, ob ich die Engineklasse als Singleton implementiert lassen sollte. Wenn ich dots Empfehlung folge, Dependency-Injection zu verwenden, müßte ich natürlich meine Enginebibliothek umschreiben und die Instanz der Engineklasse überall druchreichen, was dazu führt, dass ich beim Aufräumen der gesamten Engine (also Speicherfreigabe) gut Aufpassen muss keine Danglingpointer mehr zu haben. evt. bietet sich dann hier wohl die Verwendung eines Smartpointers an.

@BlueCobold: Ich sehe das Problem der parallelen Engines schon auch, aber das ist per Design bei mir eher so gelöst, dass ich parallele Renderer habe (evt. nicht mal das, da es ja in Ogre immer nur Root-Singleton-Objekt geben kann und parallele Viewports da drunter gelöst werden). Auf der anderen Seite könnte bei einem zukünftigen Rendererwechsel (was durch einen anderen Rendermanager prinzipiell möglich aber nicht geplant ist) die Notwendigkeit auftreten parallele Engines zu haben.
Mein Entwicklertagebuch
Aktuelles Projekt: Universum Espionage
Eingestellt:Spieleengine SilverCore
Organisator "Spieleentwickler Stammtisch Stuttgart"

8

22.08.2012, 09:17

Hm, wenn du eh alle Zeiger durchreichst, was hast du dann im Gegensatz zu einem globalen Objekt gewonnen? Wenn vom Design her alle Enginekomponenten eh auf das selbe Engine Objekt zugreifen, ist es quasi global. Der Vorteil wäre natürlich, dass du wegen der ganzen Zeiger siehst, wo es überall benutzt wird und du dadurch evtl. animiert wirst an ein paar Punkten das Design zu ändern, dass es nicht mehr benutzt wird. Aber solange du das nicht machst, hast du einfach nur ne Menge Zeigerkopiercode geschrieben und immer noch die selben Abhängigkeiten.
Lieber dumm fragen, als dumm bleiben!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

22.08.2012, 14:11

Hm, wenn du eh alle Zeiger durchreichst, was hast du dann im Gegensatz zu einem globalen Objekt gewonnen?

Viel, die Abhängigkeiten sind nun explizit in den Interfaces ausgedrückt. Man wird also dazu gezwungen, sich über Abhängigkeiten Gedanken zu machen, sich mit der Frage von Scope und Besitzverhältnissen auseinanderzusetzen. Plötzlich ist es z.B. rein prinzipiell unmöglich, Objekte in der falschen Reihenfolge zu initialisieren, da man sowas nun gar nichtmehr hinschreiben oder gar kompilieren kann; der ganze Code wird automatisch sauberer und das Design flexibler...

Wenn ich dots Empfehlung folge, Dependency-Injection zu verwenden, müßte ich natürlich meine Enginebibliothek umschreiben und die Instanz der Engineklasse überall druchreichen, was dazu führt, dass ich beim Aufräumen der gesamten Engine (also Speicherfreigabe) gut Aufpassen muss keine Danglingpointer mehr zu haben. evt. bietet sich dann hier wohl die Verwendung eines Smartpointers an.

Wenn du Probleme mit dangling Pointern bekommst, dann ist das ein Hinweis drauf, dass die Besitzverhältnisse zwischen deinen Objekten nicht wirklich klar sind. Smartpointer solltest du sowieso verwenden (std::unique_ptr)... ;)

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »dot« (22.08.2012, 14:28)


Thoran

Alter Hase

  • »Thoran« ist der Autor dieses Themas

Beiträge: 520

Wohnort: Stuttgart

Beruf: Senior Software Engineer

  • Private Nachricht senden

10

23.08.2012, 09:28

Bin zwischenzeitlich am überlegen, ob meine Engine-Bibliothek überhaupt eine Engineklasse haben sollte. Letzten Endes erscheint es mir sinnvoller in der Engine-Bibliothek nur die Manager anzubieten, die dann von der Anwendung instanziert werden und dort auch verwaltet. Damit erübrigt sich die Engineklasse als Accessor-Objekt. Bleibt allein die Frage wie ich es elegant löse, dass meine Funktionen in der Enginebibliothek zum Ermitteln der Anwendungsdateipfade möglichst überall in der Bibliothek zur Verfügung stehen. Im Zweifelsfall läfut es dabei wieder auf dots Vorschlag raus, eine Utilityklasse zu implementieren und diese per Depedency Injection durchzureichen.

Thoran
Mein Entwicklertagebuch
Aktuelles Projekt: Universum Espionage
Eingestellt:Spieleengine SilverCore
Organisator "Spieleentwickler Stammtisch Stuttgart"

Werbeanzeige