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

1

06.12.2014, 16:29

Schlechtes Design + "gefährliche" Entwurfsmuster

Hi, vor einiger Zeit habe ich begonnen "Head First Design Pattern" (E. Freeman u.A.) zu lesen und bin auch über http://gameprogrammingpatterns.com/contents.html gestolpert. In beiden wird u.A. das Singleton-Pattern thematisiert. Nun ist gerade dieses Pattern ziemlich umstritten, da es Abhängigkeiten teilweise verdeckt und zu starker Kopplung führt; d.h. Code der mit Singletons arbeitet lässt sich z.B. auch schwer mit Unit-Tests automatisiert testen.

Seht ihr auch bei anderen Pattern "Gefahren?" Mir ist klar, dass Entwurfsmuster Problemlösungen sind, d.h. angewendet werden sollten, wenn ein entsprechendes Problem vorliegt. Allerdings frage ich mich woran ich erkenneb kann, ob die Anwendung eines Entwurfsmusters mein eigentliches Problem löst - oder nur die Symptome meines schlechten Designs behandelt. Durch massive Verwendung von Singletons würde ich z.B. de facto viele globale Variablen verwenden, was designtechnisch mindestens fragwürdig ist bzw. sein kann.

Mir ist klar, dass es da kein Rezept gibt - aber vllt. ein paar Indikatoren schlechten Designs - um nicht in Versuchung zu kommen es mit Entwurfsmustern "behandeln" zu wollen :)

LG Glocke

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

2

06.12.2014, 16:50

Auch ich bin der Ansicht, dass das Singleton, so wie es häufig verwendet wird, dem Design eher schadet. Das liegt daran, dass es 2 Dinge in einem Atemzug "löst": Einzigartigkeit und globale Zugreifbarkeit. Häufig braucht man aber nur eins von beiden, viel zu oft aber eigentlich gar keins.
Auf diesen beiden Problem bauen die ganzen anderen Problem auf, wie die starke Kopplung, verschleierte Abhängigkeiten, schlechte Testbarkeit abhängiger Codestellen (man denke nur an Singletons, die wiederum Singletons verwenden), da für einen erfolgreichen Tests die Singletons fehlerfrei sein _müssen_ und auch nicht auf Testdaten oder eine Testimplementierung gewechselt werden kann.
Ich durfte mir bspw. bereits von jemanden anhören, wie gut doch der Spieler in einem Singleplayer-Spiel sich für ein Singleton anbieten würde.
Ansonsten kann ich gerade keine Entwurfsmuster nennen, die derartig zu meiden sind.

Wann welche Muster zu nutzen sind, kommt auf die Probleme an, die man lösen muss. grundsätzlich dürfte da Erfahrung schon sehr bei der Einschätzung helfen, ich kann aber auch keine allgemeingültige Regel aufstellen.

Indikatoren schlechten Designs nennt man auch "Code Smell" und diesen gibt es in verschiedenen Geschmacksrichtungen. Ein Beispiel wären Datenklassen, die nur Daten speichern, und separate Klassen, die nur auf den Daten der Datenklassen arbeiten und diese verändern (häufig irgendwas mit "Manager" im Namen).
Weiterhin gäbe es noch die "Single Responsibility" der Klassen. Ist diese nicht gegeben, ist das mal wieder Code Smell.
Mehr Dinge fallen mir da auf Anhieb nicht ein, aber es ist ja auch Wochenende... *hust*
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

3

06.12.2014, 19:15

Indikatoren schlechten Designs nennt man auch "Code Smell" und diesen gibt es in verschiedenen Geschmacksrichtungen.

Danke für das Stichwort :)

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

4

06.12.2014, 21:02

Und fast hätte ich da ein Vorzeigeprojekt vergessen, welches thematisch überaus passend sein dürfte: FizzBuzz Enterprise Edition
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

GlowDragon

Frischling

Beiträge: 66

Wohnort: Bayern

Beruf: Informatikstudent

  • Private Nachricht senden

5

06.12.2014, 22:52

Aha, jetzt weiß ich, wie man das nennt, was ich momentan anwende. ^^

Ich hab kürzlich in einem meiner Projekte angefangen, Singletons zu nutzen, um überall auf die Program-Klasse und Manager-Klassen zugreifen zu können und nun erfahre ich von den Nachteilen. Wie sieht es denn aus, wenn ich nur für die Hauptklasse, in der ich auf alle Komponenten des Programms/Spiels zugreifen kann, Singleton verwende?
Beispiel: Program.Instance.inputManager
Liegt das Problem eher an den statischen Referenzen zu den Klassen oder an den statischen Methoden? Bisher programmierte ich noch keine Modultests, deshalb meine Unsicherheit.

Tobiking

1x Rätselkönig

  • Private Nachricht senden

6

07.12.2014, 01:38

Ich kenne den Begriff Anti-Pattern für schädliche Entwurfsmuster. Wobei der Begriff, wie man in der Wikipedia Auflistung sieht, auch allgemein für schlechte Angewohnheiten bei der Softwareentwicklung verwendet wird.

Wie sieht es denn aus, wenn ich nur für die Hauptklasse, in der ich auf alle Komponenten des Programms/Spiels zugreifen kann, Singleton verwende?

Wenn die Komponenten in der Hauptklasse austauschbar sind bzw. dynamisch zugewiesen werden, wäre es sogar möglich Unit Tests zu schreiben. Man würde innerhalb der Unit Tests dann der Hauptklasse Mocks (Attrappen) für die Komponenten zuweisen. Die Hauptklasse ist dann nichts anderes als ein IoC Container. Allerdings ist die Lösung nicht besonders schön, da du so nur globalen Zustand in ein globales Objekt kapselst. Abgesehen von der Nicht-Testbarkeit behältst du alle Nachteile. Deswegen werden IoC Container häufig auch als Anti-Pattern bezeichnet, ähnlich wie es bei Singletons der Fall ist.

Das Ziel sollte sein Abhängigkeiten als Parameter im Konstruktor oder der jeweiligen Funktion zu übergeben. Ist das nicht möglich, deutet es häufig auf ein Problem in der Architektur hin. Nimmt die Zahl der Parameter überhand, tut die Klasse vermutlich zu viel. Das Ganze hört dann auf den Namen Dependency Injection, und für die meisten Sprachen gibt es Frameworks, die Parameter automatisch füllen können, so dass man z.B. sowas wie Log Objekte nicht durch die komplette Objekthierarchie durchschleifen muss.

7

09.12.2014, 16:33

Das Ziel sollte sein Abhängigkeiten als Parameter im Konstruktor oder der jeweiligen Funktion zu übergeben. Ist das nicht möglich, deutet es häufig auf ein Problem in der Architektur hin. Nimmt die Zahl der Parameter überhand, tut die Klasse vermutlich zu viel. Das Ganze hört dann auf den Namen Dependency Injection

Interessanter Standpunkt :thumbup:

8

09.12.2014, 21:27

Zitat


Indikatoren schlechten Designs nennt man auch "Code Smell" und diesen gibt es in verschiedenen Geschmacksrichtungen. Ein Beispiel wären Datenklassen, die nur Daten speichern, und separate Klassen, die nur auf den Daten der Datenklassen arbeiten und diese verändern (häufig irgendwas mit "Manager" im Namen).


Kannst du erklären was daran schleches Design ist? Und wie kann man das eleganter lösen?

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

9

10.12.2014, 00:30

Falls du die Datenklassen meinst: Wenn es Methoden/Funktionen gibt, die auf den Daten eines Typs arbeiten, dann sind diese sehr wahrscheinlich auch gut als Member innerhalb dieser aufgehoben. Um die Länge eines Vektors zu ermitteln, benötigt man zwar ein paar mathematische Operationen, dennoch wäre diese Methode oder Property am Besten innerhalb des Vektors aufgehoben.
Bei Managern ist es häufig so, dass sie einerseits Methoden für die Verwaltung der Elemente besitzen (neu hinzufügen, erzeugen, entfernen, ...), in vielen Fällen werden darin aber auch Methoden angelegt, die sich lediglich um das Anpassen der Daten eines einzelnen Elements kümmern.

Tobiking hat auch angesprochen, was mit dem Verschleiern von Abhängigkeiten gemeint ist:
Wenn eine Klasse von einer anderen abhängig ist, dann ist das auch am ehesten sichtbar, wenn die andere Klasse auch in der Schnittstelle (Parameter für den Konstruktor, Parameter eines Setters, ...) auftaucht. Nur, weil ein Parameter eingespart wird, verschwindet aber nicht die Abhängigkeit. Sie wird also weniger erkennbar/sichtbar.
Wie er auch schon richtig geschrieben hat, deuten viele Abhängigkeiten auch eher auf ein schlechtes Design hin und man muss sehr wahrscheinlich das Design an und für sich überarbeiten, statt sich einfach nur Parameter einzusparen.

@Tobiking: ja, es gibt beide Bezeichnungen. Ich würde unter einem Anti-Pattern (vergleichbar mit den Design Patterns) konkrete Lösungsansätze verstehen, die grundsätzlich schlecht sind und nicht verwendet werden sollten, während ich unter Code Smell eher Indizien für schlechtes Design sehen würde.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

BitShift

Frischling

Beiträge: 39

Wohnort: Leverkusen

Beruf: Informatiker Anwendungsentwicklung

  • Private Nachricht senden

10

10.12.2014, 10:29

Designpatterns werden IMO aber gerade auch in Hobbyprojekten zum Teil rein aus Selbstzweck eingesetzt.
Software wie z.B. Eclipse ist auch deshalb so erfolgreich, weil sie gut designed ist und implementierende Dritthersteller ebenfalls Plugin- basierte Infrastrukturen bieten wie z.B. EMF, die nur durch relativ konsequenten Einsatz von Design Patterns entsprechende Verbreitung finden. Dazu braucht's eine stabile Basis.

Was allerdings unnötig ist (aber sicherlich interessant ;) ):
In einer Anwendung oder von mir aus auch einem Spiel, dessen Code vielleicht 1-3 Leute sehen, zu viel Zeit auf Wiederverwendbarkeit und Erweiterbarkeit zu verschwenden. Dann programmiert man nämlich das Framework, aber nicht mehr das Spiel, und versenkt unzählige Stunden damit sich zu fragen, warum man trotz konsequentem Einsatz von verschiedensten Design-Patterns immer noch irgendwo casten muss...

Oder:
Zum Beispiel die Änderungen durch GameObjects per Event System / Observer Pattern einer Game Instanz mitzuteilen, z.B. um Punkte aufzurechnen. Warum nicht einfach eine shared Instanz eines Structs oder einer Klasse übergeben, der ich solche Ergebnisse direkt mitteile? Oder das Game direkt als Member der GameObjects halten? Weil es nicht SCHÖN ist? Weil ich VIELLEICHT in Zukunft noch andere Objekte haben KÖNNTE, die auf diese Änderungen reagieren sollen? Darf ich Sounds zum Beispiel nicht aus dieser shared class abspielen, weil der Soundmanager (!) da nix zu suchen hat? Ich meine, spielt es denn in diesem Kontext wirklich eine Rolle, wer hier Sounds abspielen darf, mal abgesehen von einem persönlichen Unrechtsempfinden?

Ich persönlich halte Software Architektur für eine ziemliche Bitch. Eine 100% erfolgreiche Planung anhand von Design-Patterns setzt nämlich quasi schon im Vorfeld 100% Kenntniss der Anforderungen an die Software voraus. Das gibt es natürlich so gut wie nie. Software wird also ständig an sich ändernden Anforderungen angepasst, und da kann es dann auch schonmal passieren, dass grundlegende Designänderungen stattfinden (Worst Case). In solchen Fällen hätte man wahrscheinlich mehr Zeit gespart, wenn man ursprünglich quick & dirty runterprogrammiert und danach nach Bedarf refaktorisiert hätte.

Ich hatte Anti- Patterns immer eher als eine Philosophie betrachtet: Nämlich grundsätzlich den Einsatz von Patterns zu hinterfragen und im Zweifel drauf zu verzichten... Naja.

Jedenfalls kenne ich ein paar Hobby- Projekte (inklusive eigener), die auf Grund von Overdesign an ihrer eigenen Sinnlosigkeit erstickt sind ;)

Also anstatt den Einsatz von Gettern/Settern zu diskutieren oder über den Sinn und Unsinn von Singletons zu diskutieren, fände ich es besser, wenn man für sich selbst generelle "Design"- Vorgehensweisen festlegt, da finde ich z.B. die Tips von Tobiking bzgl. der Dependencies gut, da sie auch direkt das Design beeinflussen werden. Ansonsten ist im Endeffekt eher das Ergebnis wichtig. Es ist sicherlich spaßig, sich über ein mies designtes Projekt zu beömmeln, aber wenn es ein FERTIGES Projekt ist, ist es mehr wert, als 100 unfertige, super- designte Projekte zusammen, die nie bis zur Öffentlichkeit durchdringen.

Eine Ausnahme von all diesen Regeln sind halt Projekte wie Libraries oder Projekte, die auf Grund ihrer Erweiterbarkeit ein gut durchdachtes Design benötigen, um dem User sinnvolle und eindeutige Vorgaben zu liefern, wie er mit dem Framework / der Lib zu arbeiten hat. Da darf sich nämlich meistens auch langfristig nichts mehr dran ändern.
java.lang.SignatureMakesNoSenseException: de.signatureHandler.java
caused by: User is too dumb to create a correct signature.

Werbeanzeige