Posts tagged: OOD

Joshua Bloch über API Design

Joshua Bloch hatte vermutlich schon die ein oder andere schlechte Idee. Wahrscheinlich sogar ein paar richtig bescheuerte Ideen. Und genau deshalb hat er Ahnung.

In der Java API stecken ne ganze Menge blöde Ideen, aber auch sehr gute. Typischerweise kamen zuerst die blöden Ideen wie die ursprüngliche Implementierung der Date-Klasse oder die Idee Stack von Vector abzuleiten und dann die guten, die leider etwas spät dran sind, sodass die dann nur noch die Notsitze im Namensraum gekriegt haben.

Wie viel davon Joshua Bloch nun selbst verbrochen hat, weiß ich nicht. Jedenfalls gehört er deshalb zu den Leuten, die Ahnung vom API-Design haben. Meiner Meinung nach lernt man nur aus Fehlern wirklich gut. Entweder aus den eigenen oder aus denen von anderen. Wer lieber aus den Fehlern von anderen lernen will, sollte sich vielleicht mal seinen Vortrag “How To Design A Good API and Why it Matters” ansehen. Dort beschreibt er nämlich genau das: Was man so alles falsch gemacht hat, wie man es besser machen kann und warum das nicht nur für API-Designer relevant ist, sondern (in abgeschwächter Form) für jeden Entwickler. Sehr empfehlenswert.

Den Vortrag gibts in mehreren Varianten:

Besonders gut gefallen mir die Beispiele. Da ich gerade selbst für meinen Vortrag Beispiele hab finden müssen, weiß ich, dass das viel mehr Arbeit ist, als man vielleicht denkt.

Patterns

Ü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 »

Softwareentwicklungs-Prinzipien: Eine Übersicht

Ich hatte ja schon angekündigt, dass ich mein Versprechen auf den letzten Delphi-Tagen in Raten erfüllen werde. Immer mal wieder landet etwas zu den Softwareentwicklungs-”Daumenregeln” oder -Prinzipien hier im Blog. Vielleicht werde ich die Artikelserie irgendwann in ein Tutorial packen oder auf andere Weise verarbeiten. Mal sehen.

Zuerst aber ist mal ein grober Überblick notwendig und diesen möchte ich hier geben.

Wie schon mehrfach erwähnt, sehe ich Softwareentwicklung als das ständige Ausbalancieren von “Daumenregeln” oder “Prinzipien”. Solche Daumenregeln gibt es eine ganze Menge. Ich hab bisher schon so an die hundert dieser Regeln zusammengetragen und werde die hoffentlich auch bald mal sortieren und hier posten. Diese Daumenregeln können sehr unterschiedlich sein. Manche sind sehr allgemein, andere hingegen auf ganz bestimmte Probleme spezialisiert. Viele dieser Daumenregeln, sind einfach nur Spezialisierungen oder Abwandlungen anderer, aber es ist auch nicht selten, dass sich einzelne Regeln widersprechen. Es ist dann jeweils ein geeigneter Mittelweg, ein Kompromiss zu suchen. Man kann diese Regeln auch als Kräfte betrachten, die in unterschiedliche Richtungen ziehen. So gesehen ist das Ziel ein Kräftegleichgewicht.

Das was ich hier schreibe, ist keine für alle Ewigkeiten gültige Wahrheit, an der nicht gerüttelt werden darf. Im Übrigen bin ich der Meinung, dass es so etwas gar nicht gibt. Aus nahe liegenden Gründen werde ich hier also meine persönliche Sichtweise auf die Softwareentwicklung darstellen. Man darf hier gerne anderer Meinung sein. Vieles ist Ansichtssache und auch wenn manche der Prinzipien (nicht aber alle) weithin anerkannte Lehrmeinung sind, sollte man trotzdem überlegen, ob man diese nachvollziehen kann. Im Übrigen wird man diese hier vorgestellten Daumenregeln nur dann wirklich anwenden können, wenn man die für zumindest einigermaßen sinnvoll erachtet.

Ich beziehe mich hier hauptsächlich auf objektorientierte Softwareentwicklung, prinzipiell gilt das meiste aber auch für andere Programmierparadigmen, bzw. sind leicht auf solche übertragbar. Jetzt will ich erstmal einen groben Überblick über die meiner Meinung nach wichtigsten Prinzipien geben. Genaueres folgt dann irgendwann mal in einzelnen Artikel zu den jeweiligen Prinzipien.

Die drei obersten Daumenregeln

“Die obersten drei Regeln der Softwareentwicklung”, wie ich sie hier mal nennen will, beschreiben, wie man mit den ganzen hier vorgestellten Regeln umgehen soll. weiter lesen »

Gefangen, nicht gefunden! #16

Informatik – Im Weitesten Sinne

  • Martin Fowler über Zertifikate
  • Nochmal Martin Fowler. Diesmal über die Umsetzung von Rollen. Fowler zeigt hier, wie man auf verschiedene Art und Weise Rollen umsetzen kann. Der Artikel ist darüber hinaus augenöffnend, was den Unterschied zwischen Analyse und Design angeht.
  • Wie nutzt man eigentlich Exceptions richtig? Dazu hab ich einen interessanten Artikel gefunden. Der Artikel bezieht sich auf Java, lässt sich aber leicht auf andere Sprachen übertragen.
  • Den Call for Paper für die diesjährigen Delphi-Tage hab ich schon vor einiger Zeit bemerkt. Mir schwebt auch schon etwas vor. Nur muss ich endlich mal dazu kommen, was zusammen zu schreiben und abzuschicken.
  • Eigentlich hab ich die ganze Zeit schon vor, über das Kreis-Ellipse-Problem zu bloggen. Allerdings hab ich dazu deutlich mehr Material gefunden, als ich bisher Zeit hatte zu sichten. Bis ich dazu komme, etwas zu schreiben, hier schonmal in Link.
  • Kurz und verständlich. Die Java Collections API.
  • Von der gleichen Seite: Datumswerte in Java. Hier sieht man mal wieder, was so typisch für die Java API ist: Intuitive Interfaces wurden zuerst genutzt. Die waren aber inflexibel ausgelegt. Jetzt sind die deprecated und es gibt bessere und mächtigere Interfaces. Nur sind die eben nicht mehr so intuitiv benutzbar. Die ganzen deprecated-Interfaces müllen den Namensraum zu. Jeder würde Datumsfunktionen in der Klasse Date suchen. Nur sind das eben die alten, die man nicht mehr benutzen soll… Das ist der Nachteil von Abwärtskompatibilität bzw. einer gewissen Lesart des Open-Closed-Principles: Alte Zöpfe, die niemand abschneidet. Nicht dass ich damit sagen wollte, man sollte das unbedingt tun. Das ist wie immer eine Abwägungssage. Und es zeigt, wie schwierig Frameworkdesign ist.
  • Eigentlich hatte ich ja vor, ein Tutorial über OOA und OOD zu schreiben. Sieht aber momentan so aus, als käme ich nicht dazu. Vielleicht mache ich aber auch einzelne Blogartikel draus und fasse die später doch zu einem Tutorial zusammen. Mal sehen…

Anderes

  • Ich lese gerade “Der Zauberhut” von Terry Pratchett. Das Buch ist echt klasse. Terry Pratchett schreibt ja generell tolle Bücher und gerade jetzt merke ich das mal wieder. Faszinierend.

The Tradeoff Game

Wie ich schon mehrfach erläutert habe, sehe ich Softwareentwicklung als das ständige Ausbalancieren von Prinzipien oder “Daumenregeln”. Es gibt eine ganze Menge solcher Daumenregeln (ich hab mal an die hundert solcher gesammelt) und ich hab vor, diese nach und nach hier mal vorzustellen.

Einige dieser Regeln ergänzen sich, manche sind spezieller als andere oder einfach nur eine andere Sichtweise auf eigentlich das selbe Prinzip. Viele aber widersprechen sich auch. Ein typisches Beispiel wären folgende beiden Prinzipien:

  1. mache deine Software generisch, damit sie auch leicht mit geänderten Anforderungen klar kommt; kurz: allgemeingültig ist besser als speziell
  2. mache deine Software einfach, damit sie leicht verständlich ist (KISS); kurz: einfach ist besser als kompliziert

Es wird wohl kaum jemand in Abrede stellen, dass beides wichtige Prinzipien der Softwareentwicklung sind. Wir alle wollen am liebsten einfachen Code der trotzdem sehr viel kann. In der Regel werden wir so etwas aber nicht haben können. Je generischer man versucht zu programmieren, desto komplexer wird der Code. Letztendlich streben beide Prinzipien also in unterschiedliche Richtungen. Es gilt dabei immer einen Mittelweg zu finden.

Man kann diese Daumenregeln auch als “Kräfte” bezeichnen. Dann wäre das, was wir suchen, eine Art Kräftegleichgewicht. Die Kräfte können unterschiedlich stark sein und so liegt das Gleichgewicht nicht immer in der Mitte. Außerdem gibt es meist mehr als zwei Kräfte, die es zu betrachten gilt.

Oft sind die Gewichtungen aber auch subjektiv. Manche Entwickler werden Einfachheit wichtiger finden, andere finden Generizität wichtiger. Es gibt dann mehrere “richtige” Lösungen. Aber alle werden darüber übereinstimmen, dass eine Lösung, die beide Prinzipien missachtet, eine schlechte ist. Komplizierte Software, die nur einen ganz bestimmten Spezialfall abdeckt und sich nicht auf andere Probleme anpassen lässt, wollen wir in der Regel nicht haben [1]. Wir suchen also eine Art Pareto-Optimum. Welche pareto-optimale Lösung jetzt für ein konkretes Problem gewählt werden sollte, hängt von vielen Faktoren ab. Zum einen von den konkreten Anforderungen für die zu entwickelnde Software, zum anderen aber auch projektabhängige Einflüsse wie Vorlieben, Kenntnisse und Fähigkeiten der Teammitglieder und sogar der Teamorganisation.

Dieses Ausbalancieren bezeichne ich gerne als “Tradeoff Game”. weiter lesen »

Pipe And Filter

Ü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 »

Was die OOP-Tutorials verschweigen

oder: Wie man objektorientiert denkt

Abstract

Zielgruppe: OOP-Einsteiger und -Fortgeschrittene, sowie alle, die das Gefühl haben, die OOP noch nicht ganz verstanden zu haben. Ein OOP-Tutorial sollte man aber zumindest mal gelesen haben.

Die ersten Gehversuche in der OOP fallen oft recht schwer. Und in der Tat klappt das, was dabei heraus kommt, meinst mehr schlecht als recht.
Die OOP erweitert erst einmal die Möglichkeiten, Fehler zu machen. Eine der Ursachen dafür ist, dass Objektorientierung eine andere Denkweise erfordert. Ohne diese objektorientierte Denkweise programmiert man vielleicht “prozedural mit Klassen”, nicht aber wirklich objektorientiert. Und so bleiben auch die schönen Vorteile der OOP wie Wiederverwendbarkeit und Änderbarkeit aus. Wie aber denkt man objektorientiert? Dieser Vortrag stellt einige objektorientierte Denkmuster und Prinzipien vor und zeigt, dass Objektorientierung mehr ist als Klassen und Vererbung.

Folien

Das war Thema meines Vortrags auf den diesjährigen Delphi-Tagen. Die Folien gibts wie versprochen zum Download: weiter lesen »

InterpreterDemo

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.

Warum Singletons bööse sind

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 »

Kaffeemaschinen und OOD

Heuristics and Coffee

Per Zufall bin ich auf die Seite von Robert C. Martins Firma gestoßen. Robert C. Martin das ist der Autor von Clean Code. Das Buch muss ich mir übrigens auch unbedingt mal zulegen. Das hab ich leider noch nicht gelesen.

Jedenfalls hab ich seine Seite gefunden und dort ne Menge interessante Artikel gefunden. Unter anderem auch einen der nennt sich Heuristics and Coffee. Das ist ein Kapitel aus seinem Buch UML for Java Programmers.
weiter lesen »

powered by WordPress | QuickLaTeX | WordPress Themes