Anti-Patterns mehrere Punkte
-
Ich hätte mal eine generelle Frage zum Thema Anti-Patterns:
http://de.wikipedia.org/wiki/Anti-PatternWenn man sich die aufgezählten Einzelbeispiele so durchliest, kommt man
ja eigentlich zu dem Schluss, dass diese Vorgangsweisen, wie man ein
Projekt nicht umsetzen soll, in der realen Praxis doch eigentlich
die NORM sind. Also ich kenne kaum ein (quelloffenes) größeres Projekt, in
dem nicht mindestens 50% der hier aufgezählten, angeblichen Anti-Patterns
umgesetzt werden. Betimmt verhält es sich mit kommerzieller, closed-source
Software auch nicht viel anders.Mehrere Fragen:
(1) Ist es nicht eher ein utopisches Wunschdenken, dass man eine
reale, komplexe Anwendung unter komplettem Verzicht auf diese 'Anti-Patterns'
(God Class, Spaghetticode, Wunderwaffe, Erdölraffinerie usw.) je umsetzen kann?(2) Gibt es eigentlich objektive/generell gültige Kriterien, ab wann es sich
bei einem konkreten Stück Quellcode um eines dieser 'Anti-Patterns' handelt,
oder ist das von der subjektiven Willkür eines super-kritischen Quellcode-Lesers
abhängig? Es ist ja so, dass für manche Leute ja bereits jede doppelte
Schachtelung von Schleifen zu komplex ist, während andere Leute erst
bei drei- oder vierfacher Schleifen-Schachtelung den Durchblick verlieren.
Die ersteren würden also wohl schon eine doppelte Schachtelung als
Spaghetti-Code bezeichnen, für andere Leute muss ein Schleifenkonstrukt schon
wesentlich tiefer sein, bevor sie an der Code-Qualität zu zweifeln beginnen.(3) Hat diese ganze Anti-Pattern Thematik nicht eigentlich einen ziemlich
'scherzhaften' Unterton, d.h. geht's dabei primär darum, dass man sich über
Quellcode lustig macht, den man selber nicht versteht (weil man ihn nicht
entwickelt hat + weil er schlecht dokumentiert ist), oder kann Quellcode
auch ganz unabhängig davon in so eine Anti-Pattern Kategorie fallen?
Objektive Kriterien ?(4) Sind diese Anti-Patterns eigentlich nur für die objektorientierte
Programmierung von Belang, oder auch für andere Programmierparadigmen?
Weil ja eigentlich jeder prozedurale Quellcode laut diesen Kriterien
schon mal grundsätzlich ein Anti-Pattern wäre ...(5) Was insbesondere bei dem wiki-Artikel fehlt (und wozu man im Netz auch
kaum etwas findet) sind ganz konkrete, Real-World-BEISPIELE, besser noch
PARADE-BEISPIELE für Quellcode, der in eine dieser Kategorien fällt und
deshalb ein Anti-Pattern ist. Oder auch die generelle Struktur von bekannten
APIs, Game Engines, Betriebssystemen usw. Z.B. lässt sich ja unter Windows
ohne eine Instanzierung der Klasse gdi32.dll rein gar nichts grafisches
auf dem Bildschirm ausgeben. Könnte man also sagen: gdi32.dll ist eine GodClass,
weil ohne ihrer Einbindung gewisse Programmier-Aufgaben gar nicht machbar sind?--- Meine aller freundlichste Einladung zum (gerne auch scherzhaften)
Smalltalk zu diesem Design-Anti-Patterns Thema, weil ich find's einfach nur
witzig, dass die überwältigende Mehrheit der RL-Software mit diesen scheinbar
ungeeigneten Patterns erstellt wurde und trotzdem oft tadellos funktioniert.
Also ganz so schlimm kann ja dann so eine Programmier-Sünde wie z.B. eine
1000-Zeilen C-Funktion doch nicht sein, richtig? ---
(Btw. erprobe gerade selber ein - wie ich vermute - bisher unversuchtes
Design-Pattern: Eine Klasse, die anstatt public Funktionen lieber
public Funktionszeiger anbietet, die sich dynamisch zur Laufzeit ändern
können (also unter dem gleichen Namen je nach Zustand des Objekts was anderes
tun können). Keine Ahnung, wie man sowas nennen müsste, vermutlich
'Chameleon-Pattern', und womöglich ist's ja auch ein Anti-Pattern, sowas
zu machen ...)mfg
-
@1 ja, ist grösstenteils Wunschdenken. Allerdings gibt es immer Ausnahmen. Wichtig ist aber IMO nicht dass eine grosse Applikation frei von jeglichen Anti-Patterns ist, sondern dass bestimmte Teile der Applikation frei von besonders verherenden Anti-Patterns (bzw. "Designfehlern") ist. Wenn z.B. die Basis schon voll mit fragwürdigen Code ist, dann kann man darauf kaum was vernünftiges aufbauen. Wenn dagegen nur Schichten "ganz oben" z.B. von mistigem Code durchzogen sind (z.B. diverse Dialoge, oder auch abgekapselte Teile wie die Implementierung einer Rechtschreibprüfung), dann ist das weniger tragisch.
@2 Alles ist subjektiv
@3 Zum grossteil ist das gar nicht scherzhaft. Sind halt Pattern wo Leute beobachtet haben, dass, wenn man sie verwendet, man schnell Probleme bekommt. Zu behaupten all das wäre scherzhaft wäre zu behaupten dass es keinen schlechten Code gibt.
@4 Es gibt für alles "Anti-Pattern", selbst für Dinge die mit Programmieren nix zu tun haben. Es gelten nur nicht überall die selben Dinge als schlecht. Wenn man mit einer prozeduralen Sprache arbeitet, wird es wohl kaum ein schlimmer Fehler sein prozeduralen Code zu schreiben. Viele "Anti-Patterns" sind allerdings recht abstrakt, so dass sie sich auf (fast) alle Programmiersprachen anwenden lassen. "Globale Variablen = böse" gilt z.B. für quasi alle Sprachen.
@5 gdi32.dll ist keine Klasse
Und nein, das wäre auch kein Anti-Pattern. "Muss Klasse X instanzieren bevor ich was X-bezügliches machen kann" ist nicht die Definition einer Gott-Klasse. Eine Gott-Klasse ist eine Klasse die zu viele Aufgaben in sich vereint. Um bei GDI zu bleiben: wenn du eine einzige Klasse machst, die sämtliche GDI Funktionen bereitstellt, das wäre eine Gott-Klasse. Wenn du dir die GDI Funktionen allerdings ansiehst, und anhand ihres "primären" Parameters (meist ein Handle, meist der erste Parameter) Klassen zuordnest, dann kommen da nur wenige bis gar keine Gott-Klassen raus.
@--- Wenn du dein Chamäleon soweit abänderst, dass die Funktionszeiger private sind, und über public Wrapper-Funktionen aufgerufen werden, dann bist du fast schon beim State Pattern. Die Zeiger selbst public zu machen ist IMO einfach nur doof und fordert geradezu Missbrauch heraus. (Man könnte die Zeiger ja dann auch von aussen ändern, oder Clients könnten sie sich merken und dann Änderungen nicht mitbekommen etc.)
-
hustbaer schrieb:
Globale Variablen = böse" gilt z.B. für quasi alle Sprachen.
Wird damit eigentlich vorhandener globaler Zustand kritisiert, oder dass der globale Zustand oft in primitiven Variablen ohne Zugriffsschutz implementiert wird?
Weil globaler State ist ja oft keine Erwägungssache, sondern lässt sich meistens gar nicht vermeiden, z.B. Einstellungen der Software. Eine angeschlossene Datenbank ist ja oft auch nur eine Art "globale Variable", nur dass sowas dann wieder nicht verpönt ist.
-
Mal nachgehakt schrieb:
Wird damit eigentlich vorhandener globaler Zustand kritisiert, oder dass der globale Zustand oft in primitiven Variablen ohne Zugriffsschutz implementiert wird?
Letzteres. Globale Variablen bringen viele Probleme mit sich (schlechte Überwachung der Zugriffe und damit schwieriges Debuggen, Threadsicherheit, grosse Abhängigkeiten der Programmkomponenten...).
Globaler Zustand ist eigentlich immer vorhanden und an sich nichts Schlechtes. Man muss ihn an der richtigen Stelle behandeln, genauer gesagt an möglichst wenig Orten. Bei guter Kapselung kann man trotz globalen Zuständen schön objektorientiert arbeiten. Zum Beispiel war OpenGL vor allem in den Anfängen eine einzige State-Machine mit globalem Status, trotzdem arbeiten darauf aufbauende Bibliotheken mit eigenständigen, lokalen Objekten (Meshes, Sprites, Texturen...).
-
@Mal nachgehakt:
Damit meine ich, dass zu oft angenommen wird dass es Dinge "eh nur ein mal gibt", und diese dann als globale Variable oder Singleton ausgelegt werden.
Wie darauf zugegriffen wird ist dabei erstmal nebensächlich.
Genau so ob das ganze threadsafe gemacht wird oder nicht. Wobei es natürlich doppelt schlimm ist wenn es nicht threadsafe ist.Guck dir beispielsweise die DevIL (aka. OpenIL) an. Der ganze state (aktuelles Bild, die States der Library etc.) ist global. So richtig global, also nichtmal "threadlocal". Das ist das KO für viele Projekte, wo entweder viele Threads die Image-Library gleichzeitig verwenden sollen (ohne sich gegenseitig durch das Locken einer globalen Mutex auszubremsen), bzw. wo Programm-Komponenten unabhängig voneinander entwickelt werden (wenn ich Komponente A entwickle, und die DevIL verwende, und ein anderer Komponente B entwickelt, und ebenso die DevIL verwendet, und irgendwann Komponenten A und B gleichzeitig genutzt werden, dann knallts.)
Nochmal konkret dazu
Weil globaler State ist ja oft keine Erwägungssache, sondern lässt sich meistens gar nicht vermeiden, z.B. Einstellungen der Software
Die Einstellungen einer Applikation müssen doch bitte nicht global sein. Es gibt ein Applikations-Objekt und ggf. ein User-Objekt, da kann man Einstellungen speichern (je nachdem welchen Scope sie haben).
Dann kann man auch mal die gleiche Applikation 2x im selben Prozess laufen lassen.
Bei vielen Applikationen wird das nie vorkommen, bei einigen aber schon.Ganz schlimm wird es dann, wenn jemand Library-Code unter solchen Annahmen schreibt. Also ne Library baut, die diverse User- oder Applikations-Einstellungen einfach in globale Variablen klatscht. Dann kann man seine Library nämlich u.U. für Serveranwendungen und dergleichen vergessen, weil die Annahme "gibts nur ein mal" nicht mehr zutrifft.
----
Mir ist schon klar dass es immer irgendwo einen Punkt gibt, wo vorausgesetzt wird dass es etwas nur 1x gibt. Nur diesen Punkt sollte man ganz weit zurückdrängen. Darum geht's.
-
Nexus schrieb:
Globale Variablen bringen viele Probleme mit sich (schlechte Überwachung der Zugriffe und damit schwieriges Debuggen
Wenn man einen halbwegs vernünftigen Debugger hat, ist das kein Problem.
-
also schrieb:
Nexus schrieb:
Globale Variablen bringen viele Probleme mit sich (schlechte Überwachung der Zugriffe und damit schwieriges Debuggen
Wenn man einen halbwegs vernünftigen Debugger hat, ist das kein Problem.
"Debuggen" kann viel mehr bedeuten als "etwas im Debugger laufen lassen".
Globale Variablen ohne Zugriffsschutz (bzw. allgemein Daten auf die man "einfach so" zugreifen kann) können ein Programm sehr unübersichtlich machen. Der Debugger hilft da oft gar nicht, da man unmöglich alle Pfade die ein Programm nehmen kann im Debugger durchspielen kann.
-
hustbaer schrieb:
also schrieb:
Nexus schrieb:
Globale Variablen bringen viele Probleme mit sich (schlechte Überwachung der Zugriffe und damit schwieriges Debuggen
Wenn man einen halbwegs vernünftigen Debugger hat, ist das kein Problem.
"Debuggen" kann viel mehr bedeuten als "etwas im Debugger laufen lassen".
Globale Variablen ohne Zugriffsschutz (bzw. allgemein Daten auf die man "einfach so" zugreifen kann) können ein Programm sehr unübersichtlich machen. Der Debugger hilft da oft gar nicht, da man unmöglich alle Pfade die ein Programm nehmen kann im Debugger durchspielen kann.
Die Begründung ergibt keinen Sinn. Man kann auch nicht bei allen möglichen anderen Variablen alle Pfade durchspielen, wenn man ein etwas komplexeres Programm hat. Ist einfach ein zeitliches Problem.
Wenn ich wissen will, wer eine globale Variable ändert, setze ich einfach einen Data Breakpoint. Das geht sogar viel einfacher, als festzustellen, wer ein Member einer bestimmten Instanz ändert. Die Adresse der globalen Variable hab ich einfach, die einer Membervariable nicht so schnell. Einen Breakpoint in einem Setter setzen bringt da oft nicht viel, wenn es tausende Instanzen gibt und mich nur eine interessiert. Soll jetzt nicht bedeuten, dass globale Variablen gut sind, aber debuggen wird deshalb auch nicht schwerer.
-
Schneiderei11 schrieb:
(3) Hat diese ganze Anti-Pattern Thematik nicht eigentlich einen ziemlich
'scherzhaften' Unterton, d.h. geht's dabei primär darum, dass man sich über
Quellcode lustig macht, den man selber nicht versteht (weil man ihn nicht
entwickelt hat + weil er schlecht dokumentiert ist),Einen scherzhaften Unterton - ja, sicherlich.
Das hat aber Methode. Die pragmatischen Projektleute in der Softwarewelt kommen in der Fachliteratur überwiegend aus den USA, und dort ist der knochentrockene Stil nicht so üblich. Außerdem ist es einfacher den Leuten ihre Fehler vorzuhalten, wenn man es "nett verpackt".
Schneiderei11 schrieb:
(4) Sind diese Anti-Patterns eigentlich nur für die objektorientierte
Programmierung von Belang, oder auch für andere Programmierparadigmen?
Weil ja eigentlich jeder prozedurale Quellcode laut diesen Kriterien
schon mal grundsätzlich ein Anti-Pattern wäre ...Du schreibst hier ständig "Quellcode" - das Konzept eines Antipattern geht durchaus über "Quellcode" oder "schlechten Quellcode" hinaus.
Der Quellcode kann im Rahmen eines Antipattern sogar sehr gut, qualitativ hochwertig sein - trotzdem kann das Projekt an "Death-March" oder "Scope-Creep" leiden.
Bei Antipatterns handelt es sich um fehlerhafte Verhaltensweisen beim Lösen von Problemen, die man aber immer wieder gerne anwendet weil sie sich gut und brauchbar anfühlen.
Insofern ist das nicht auf OOP beschränkt - man kann sogar sagen, daß viele der Antipattern als "globale Projektverhaltensfehler" nicht einmal auf die Softwarewelt beschränkt sind.
Als Beispiel, das Thema "Scope-Creep" ist auch häufig in der Bauindustrie anzutreffen.
-
erstmal bestes Danke für die instruktiven Antworten.
globale variablen sind oft unvermeidlich, wenn mehrere threads ihre jeweiligen
'neuesten Erkenntnisse' untereinander kommunizieren sollen. in einer einzel-
thread anwendung kann man die globalen variablen aber häufig vermeiden.bezüglich meines chameleon objekts ist auch das problem, dass es genau so
eine globale variable ist, wo bei einem parallelen listen()-thread kommandos
von einem server kommen, und dann soll der listen()-thread die update()-Schnitt-
stelle von dem chameleon austauschen dürfen, aber die update() Schnittstelle
muss auch immer genau so heißen, weil sie von einemm Framework regelmäßig
angesprungen wird. naja, ist halt eine schwierige angelegenheit und mir
ist erstmal nur die möglichkeit mit dem austauschen der funktionszeiger
(durch den listen() thread) eingefallen. Leider können die funktionszeiger
auch von anderen stellen im programm ausgetauscht werden (auch von dort, wo
eigentlich nur
'lesend' mit ->update() gearbeitet werden sollte.)
@hustbaer - werde mich dann mal genauer mit dem state-entwurfsmuster beschäftigen, vielleicht lässt sich mein programm damit ja auch 'sauberer'
lösen.
mfg
-
Schneiderei11 schrieb:
@hustbaer - werde mich dann mal genauer mit dem state-entwurfsmuster beschäftigen, vielleicht lässt sich mein programm damit ja auch 'sauberer'
lösen.
mfgSchau Dir auch das Strategy- und das Bridge-Muster an. Das sind zwei weitere Ausprägungen "deiner" Idee. Die wesentlichen Unterschiede bestehen darin, wer das Verhalten zur Laufzeit austauscht und zu welchem Zweck die drei Pattern verwendet werden. Der Aufbau ist fast identisch.
-
Schneiderei11 schrieb:
--- Meine aller freundlichste Einladung zum (gerne auch scherzhaften)
Smalltalk zu diesem Design-Anti-Patterns Thema, weil ich find's einfach nur
witzig, dass die überwältigende Mehrheit der RL-Software mit diesen scheinbar
ungeeigneten Patterns erstellt wurde und trotzdem oft tadellos funktioniert.Das ist eine (imho) fehlerhafte Wahrnehmung. Die überwältigende Mehrheit aller Software Projekte im RL SCHEITERT. Und irgendwann haben sich halt Leute hingesetzt und der Frage gestellt: Warum ist das so? Was haben diese Projekte gemeinsam? Daraus sind Anti-Patterns entstanden. Getreu dem Satz: Der kluge Mensch lernt aus seinen Fehlern, der weise Mensch aus den Fehlern der Anderen.