Gerade gehört: SoftwareArchitekTOUR: Patterns in der Java-Welt. Vor einiger Zeit hab ich die ersten beiden Episoden gehört. Jetzt hab ich mir mal diese angetan.
Und natürlich kann ich mich gewisser Kommentare nicht enthalten:
- Double-Checked-Locking wird erklärt. Allerdings wird nicht darauf verwiesen, dass das Pattern kaputt ist. Zumindest wenn man kein volatile benutzt. Nähere Infos in der Wikipedia
- Es wird davon gesprochen, dass das get/set-Idiom dazu verleitet, es überzuverwenden. Also man macht überall Getter und Setter und vergisst dabei, der Klasse sinnvolle Methoden zu verpassen. Die Problematik sehe ich auch. Ich frage mich nur momentan, ob Properties wie in Delphi/C#/Ruby/etc. hier einen Einfluss haben und wenn ja welchen. Positiv oder Negativ? Ich vermute einen positiven Einfluss, bin mir da aber nicht ganz sicher.
- Immutability wird IMHO leider schlecht erklärt. Es geht natürlich nicht nur darum, dass Objekte mit gleichem Wert gleich sind, sondern auch, dass die Objekte (mehr oder weniger) zustandslos realisiert werden.
- DependencyInjection hätte ich wahrscheinlich nicht verstanden, wenn ich nicht schon wüsste, was es ist. Generell scheint es mir so zu sein, dass der Podcast nur einen groben Überblick und ein paar Buzzwords liefert. Das hört sich jetzt wie vernichtende Kritik an, aber so ist es nicht gemeint. Überblick geben ist gut. Viel mehr kann man in ner dreiviertel Stunde auch nur schwer abhandeln. Man darf nur nicht mit falschen Erwartungen an den Podcast rangehen.
- Zu Multiple Dispatch hab ich schonmal was geschrieben.
Überblick
Motivation
Es soll vorkommen, dass man in der Softwareentwicklung Probleme lösen muss. Und es soll vorkommen, dass man ein Problem, dass man schonmal gelöst hatte, nochmal lösen muss. Und es soll vorkommen, dass sich manche Probleme ähneln. Typischerweise versucht man, wenn man ein Problem zum zweiten Mal lösen muss, schneller zu sein als beim ersten Mal. Das nennt sich “lernen”.
Aber es gibt noch eine weitere Möglichkeit, beim zweitem Mal Arbeit zu sparen: Wiederverwendung. Besteht die Lösung eines Problems in einem Stück Code, dann kann man diesen verallgemeinern (parametrisieren) und beim zweiten Mal einfach hernehmen und benutzen. Das Ergebnis ist dann eine Prozedur, eine Klasse oder allgemein: ein Modul.
Aber auch, wenn die Lösung nicht in einem Stück Code besteht, sondern in einer bestimmten Herangehensweise, einer bestimmten Strukturierung oder Idee, so kann man sie wiederverwenden. Das Gegenstück zum Modul, dem wiederverwendeten Code, ist das Pattern bzw. Muster, sozusagen die wiederverwendete Idee. weiter lesen »
Jeder, der schon eine Weile programmiert, wird die Situation kennen: Man liest Code (entweder fremden oder eigenen) und es läuft einem kalt den Rücken runter, die Zehnägel stellen sich auf und man will nur noch wegsehen. Wer das noch nie erlebt hat, sollte einfach mal seine ersten Programmierversuche wieder herauskramen.
Mancher Code scheint einfach zum Himmel zu stinken. Treffenderweise nennt man so etwas “Code Smell”. Letztens habe ich bei Nick Hodges von einem Stackoverflow-Thread über solche Code Smells gelesen.
Ich dachte mir, das passt wunderbar zu der an den Delphi-Tagen angekündigten (und leider immer noch nicht wirklich begonnenen) Artikelserie zu den Prinzipien der (objektorientierten) Softwareentwicklung. Wahrscheinlich werde ich zu jedem diskutierten Prinzip – wenn möglich – ein paar Code-Smells als abschreckendes Gegenbeispiel benennen. Hier aber erstmal ein paar allgemeine Gedanken zu Code Smells:
weiter lesen »
Über “Pipe-and-Filter” wollte ich eigentlich schon ne ganze Zeit lang was schreiben. Bei meinem Vortrag auf den Delphi-Tagen hab ich das Pattern auch wieder erwähnt und so will ich jetzt mal ein bisschen was darüber erzählen.
Manche Programme scheinen bestimmte Herangehensweisen, ja sogar Programmierparadigmen zu bevorzugen. Bei meinem Vortrag habe ich eine Software zum Verwalten von Büchern (erfassen, ausleihen, zurückgeben, etc.) erwähnt. Das Problem scheint geradezu nach Objektorientierung zu verlangen, weil Bücher eben ziemlich eindeutig auch wirklich Objekte sind. Und Leser. Und Ausleihvorgänge.
Daneben gibt es aber auch Programme, die zuerst einmal gar nicht nach Objektorientierung aussehen. Einfach weil die Problemstellung mehr prozedural oder funktional erscheint. Ein Beispiel wäre ein Programm, das eine Datentransformation vornimmt.
Ich habe z.B. mal ein Programm geschrieben, das Daten von einem Oszilloskop aus einer Textdatei ausliest und über mehrere Schritte auswertet. Zuerst müssen fallende und steigende Flanken des Signals ermittelt werden, dann bestimmte Zeitintervalle bestimmt und daraus schließlich ein Endwert berechnet werden.
Die Problemstellung scheint typisch funktional oder prozedural zu sein. Trotzdem kann man das auch sehr schön objektorientiert lösen. Eben mit dem Pipe-and-Filter-Pattern.
weiter lesen »
Im letzten Semester habe ich eine Seminararbeit zum Thema “Software Architectural Tactics and Patterns for Safety and Security” geschrieben. Grob gesagt geht es darum, wie man auf Architektur-Ebene Sicherheitsaspekte (Safety und Security) behandeln kann.
Dabei stürze ich mich hauptsächlich auf die vom SEI eingeführten Tactics. Das sind eigentlich nicht viel mehr, als wiederverwendbare Ideen um bestimmte Qualitätsziele (wie eben auch Sicherheit) zu erreichen.
Tactics sind sehr feingranular und deshalb sehr flexibel, haben aber den Nachteil, dass sie auch wenig konkret sind. Theoretisch kann man sich aus einzelnen Tactics Patterns zusammenbasteln und so quasi maßschneidern. Für dieses Maßschneidern gibts aber nicht allzu viel Hilfestellung (wie das bei Patterns der Fall wäre).
Eine Taktik wäre beispielsweise “Authorization” um unbefugten Zugriff zu verhindern. Wie die Autorisierung aber nun funktionieren soll, sagt die Taktik nicht. Es ist wirklich nur die Idee, nicht die Realisierung derselben, die hier wiederverwendet wird.
IMHO ist der Wert solcher Taktiken deshalb deutlich geringer, als der Wert von Patterns. Dennoch finde ich solche Taktik-Kataloge ganz interessant. Einfach um mal nach zu gucken, ob man auch an X und Y gedacht hat…
Auf knapp 12 Seiten kann ich selbstverständlich nicht allzu viel präsentieren und auch nahe liegenden Gründen ist das auch recht akademisch. Insgesamt ist die Seminararbeit also mehr eine Zusammenfassung der Problematik und möglicher Lösungsansätze. Ich hab mir auch schon überlegt, ob ich da drüber ein etwas praktischer orientiertes Tutorial schreibe, aber da gibts wohl vorher noch diverse andere Themen, die sich besser für Tutorials eigenen. Hier also erstmal meine Seminararbeit. Vielleicht interessiert sie ja doch den einen oder anderen:
Download:
Software Architectural Tactics and Patterns for Safety and Security (Größe: 1.52 MB; bisher 522 mal heruntergeladen)
Vor einiger Zeit hab ich mal Multimethoden vorgestellt. Dabei hab ich auch angedeutet, dass man Multiple Dispatch über das Visitor-Pattern simulieren kann. Das hab ich jetzt mal selbst gebraucht. Da bietet sich direkt mal die Gelegenheit, das vor zu führen. Der Einfachheit halber bleibe ich bei meinem Asteroids-Beispiel.
Normalerweise gibt es beim Visitor-Pattern zwei getrennte Hierarchien: Elemente und Visitor. In unseren Fall fallen beide Hierarchien zusammen. Wir haben nur eine Hierarchie, nämlich die Hierarchie der Objekte, die kollidieren können. Im folgenden betrachten wir daraus Raumschiffe und Asteroiden.
Das Problem ist ja eigentlich, wie im oben verlinkten Blog-Post erläutert, dass der dynamische Typ des Parameters bzw. der Parameter nicht bekannt ist. Zumindest nicht direkt. Der Parameter selbst kennt natürlich seinen dynamischen Typ. Und das ist auch schon die Idee, die man braucht um Multiple Dispatch zu simulieren: Der Parameter weiß den Typ, also kann der Parameter auch gleich die ganze Arbeit machen.
Und so sieht das Ganze im Code aus:
weiter lesen »
Nachdem in letztens ja gegen Singletons gewettert habe, habe ich nun selbst mal eines implementiert. Wie im verlinkten Artikel erläutert, gibt es ein paar reale Einsatzszenarien, in denen sie wirklich hilfreich sind. Einen dieser Fälle hatte ich: Ich wollte eine prozedurale API (konkret ging es um MPI) kapseln.
Bei der Realisierung habe ich mich für Martin Fowlers Ansatz entschieden, da dieser, wie im angesprochenen Artikel erläutert, die Testbarkeit erhöht. Im Folgenden nochmal etwas ausführlicherer Pseudocode und eine Beschreibung zu diesem Ansatz:
weiter lesen »
In letzter Zeit hab ich mich ein bisschen mit Design Patterns beschäftigt. Dabei bin ich auch auf das Interpreter-Pattern gestoßen. Und da ich Parser ebenfalls interessant finde und eh mal ein bisschen C++ wiederholen musste, hab ich ne kleine Demo geschrieben. Gezeigt wird eine Hook-Methode (GoF:325), ein LL(1)-Parser und das Interpreter-Muster.
Das Programm parst arithmetische Ausdrücke in Infix-Notation und wertet sie aus. Beispiel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| user@host:~$ ./interpreter
Usage: interpreter "<expression>"
<expression> is a mathematical expression in infix-notation.
It may contain +, -, *, / and parentheses.
Only integers are allowed.
Example: interpreter "42-(3*(2+7)-56)"
user@host:~$
user@host:~$ ./interpreter "42-(3*(2+7)-56)"
71
user@host:~$
user@host:~$ ./interpreter "42-(3%*(2+7)-56)" # Das Programm erkennt Syntaxfehler
Unrecognized character: '%'
user@host:~$
user@host:~$ ./interpreter "42++2" # Das Programm erkennt Syntaxfehler
Number expected but Plus found |
Der Tokenizer prinzipiell relativ einfach gestaltet. Er geht den übergebenen String durch und scannt nach bekannten Tokens. Um die Klasse Tokenizer ableitbar zu machen, wird eine Hook-Methode readCustomTokens() eingeführt, die in Subklassen überschreiben werden kann um zusätzliche Tokens erkennen zu können.
Der Parser benutzt den Tokenizer um aus den einzelnen Tokens einen Syntaxbaum aus Expression-Objekten aufzubauen. Dazu bedient er sich der Technik des rekursiven Abstiegs, was die Implementierung relativ leicht macht. Zu jeder Produktion der Grammatik existiert eine Methode, die sie parst.
Die folgende Grammatik wird vom Parser akzeptiert:
1 2 3 4 5
| expression = firstPartOfExpression {('+'|'-') term} ;
firstPartOfExpression = ['+'|'-'] term ;
term = factor {('/'|'*') factor} ;
factor = 'number' | parenthesis ;
parenthesis = '('expression')' ; |
Wobei “number” hier ein Terminalsymbol ist, das der Tokenizer erkennt. “number” sieht dann folgendermaßen aus:
1
| number = '0'|'1'|...|'9' {'0'|'1'|...|'9'} ; |
Um dann auch den Syntaxbaum auswerten zu können, d.h. konkret: um das Ergebnis zu berechnen, definiert Expression eine Methode interpret(), die den Syntaxbaum rekursiv abarbeitet. Das ist letztendlich genau das Interpreter-Pattern (GoF:243) der Viererbande.
Näheres zum Code kann man der Dokumentation entnehmen. Diese erstellt man am einfachsten mit make doc.
Der Code kann beliebig verwendet werden. Download: InterpreterDemo (Größe: 37.28 KB; bisher 84 mal heruntergeladen) Viel Spaß damit.
Ich schätze mal, ich bin nicht der einzige, der von den 23 GoF-Patterns das Singleton-Pattern als erstes kennen gelernt hat. Aber woran liegt das? Vielleicht, weil es einfach ist? Und bequem? Und natürlich weil man es wunderbar dazu verwenden kann, sich um richtige OO herum zu drücken…
weiter lesen »