Tuesday, November 01, 2011

Haskell | Beispiele zu Matrizen und Vektoren

Schreiben Sie folgende Haskell-Rechenvorschriften:
1. Eine Haskell-Rechenvorschrift:
Angewendet auf ein Argument L liefert anp1 als Resultat einen Wert M vom Typ Matrix ab, in dem alle Komponentenlisten von L bis zur Länge l der längsten Komponentenliste am Ende mit Nullen aufgefüllt sind. Die längste(n) Komponentenliste(n) bleibt/en unverändert. Auf diese Weise liefert anp1 eine Matrix vom Typ  (lengthL, l) ab. Ist L die leere Liste, ist das Result von anp1 der Wert [[1]]. Folgende Beispiele illustrieren das gewünschte Ein-/Ausgabeverhalten:



Lösung

Monday, August 16, 2010

GOOS – Gedanken und Zusammenfassung des zweiten Teils

GOOS – Blogeintrag 1

GOOS – Blogeintrag 2

TDD ist ja absolut keine neue Technik um Software zu entwickeln, meines Erachtens allerdings die einzig richtige Vorgehensweise, um auch über einen längeren Zeitraum ein Projekt wartbar zu halten. Damit meine ich nicht nur Bugs bereinigen, sondern auch neue Features hinzufügen. Wenn man es daher genau nimmt, dann wird ja ein Projekt ab dem Zeitpunkt des ersten Releases gewartet. Daher sollte man der Wartung besondere Aufmerksamkeit widmen.

Im zweiten Teils des Buchs beschreiben die Autoren ihre persönlichen Sichtweises bezüglich TDD und Objektorientierter Programmierung. Einige Dinge werden dabei immer wieder betont und wiederholt. Ich habe schon einige Bücher zur Objektorientierung und TDD gelesen, allerdings haben mich die Sichtweisen dieser beiden Autoren am meisten begeistern und überzeugen können. Das liegt zuletzt mit Sicherheit an der langjährigen Projekterfahrung der Autoren mit diesen Techniken. Wenn man selbst schon einige Erfahrung mit TDD machen konnte, hat man sich mit Sicherheit auch schon gefragt, wie man TDD bei größeren (Enterprise)Anwendungen einsetzen würde.

TDD für ein neues Projekt

image

Eigentlich freut man sich ja als Entwickler ein “Greenfield”-Projekt starten zu können. Aber wie setzt man TDD ein wenn noch absolut keine Infrastruktur vorhanden ist? Wenn man ein neues Feature beginnen möchte, dann fängt man ja mit einem Akzeptanztest (End-To-End) an. Ein solcher sollte so “End-To-End” wie möglich sein, d.h. Build, Deploy und Test sollte ebenfalls im Test beinhaltet sein. Doch beim absolut ersten Feature ist man sich der benötigten Infrastruktur noch nicht 100% sicher. Also wie kann ich dann mit einem Akzeptanztest beginnen, der so “End-To-End” wie möglich, allerdings noch keine Infrastruktur vorhanden ist?

Zu diesem Zeitpunkt führen die Autoren den Begriff des Walking Skeletons” ein. Dabei handelt es sich um die dünnste Schicht echter Funktionalität, die man Bauen, Deployn und Testen kann. Wichtig ist dabei, dass man dafür nicht zu viel Zeit verbraucht. Es sollte sich dabei mehr oder weniger um eine “Whiteboard”-Architektur der Anwendung handeln, die mit der Zeit natürlich wachsen und verbessert werden kann. Wichtig ist nur, dass alle Entwickler eine gemeinsames Verständnis der Architektur und der damit verbunden Infrastruktur entwickelt haben. Sauberkeit und Ausdrucksstärke des ersten Akzeptanztests, der diese Infrastruktur beinhaltet, ist dabei noch nicht so wichtig.

Dieses so genannte “Walking Skeleton” soll also gar nicht perfekt sein. Jedoch ist der dabei entstehende Lernprozess und das Feedback extrem wichtig für den Projektfortschritt. Okay, jetzt wissen wir also wie wir bei einem neuen Projekt mit Hilfe von TDD und mittels eines “Walking Skeleton” den ersten Entwurf unserer Architektur entwickeln. Wie geht es nun weiter? Wie halten wir diesen Prozess am Laufen?

TDD Prozess am Laufen halten

image

Grundsätzlich fängt man von Außen nach innen an. Für jedes neue Feature entwickelt man zuerst einen neuen  fehlschlagenden Akzeptanztest. Wichtig dabei ist, diesen Test möglichst technologieneutral zu implementieren, d.h. nur Ausdrucksweisen der Domäne zu verwenden. Der Vorteil von Akzeptanztest sollte bereits klar sein: Die Präzision Anforderungen in automatisierten Tests zu spezifizieren hilft implizite Annahmen zu vermeiden.

Ganz wichtig ist dabei auch die Trennung zwischen Unit Tests und Akzeptanztests, die ja den Projektfortschritt anzeigen und daher solange fehlschlagen sollten, bis das neue Feature fertig implementiert ist. Im Gegensatz zu Unit- und Integrations-Tests, die schnell und immer sauber sein sollten und dem Entwickler zur Unterstützung dienen.

Weitere wichtige Empfehlung der Autoren sind: Man sollte immer mit dem dem einfachsten erfolgreichen Fall anfangen (nicht mit Fehlerfällen) zu testen. Tests sollten so geschrieben werden, wie man sie selbst gerne lesen würde und Fehlermeldungen sollten solange verbessert werden, bis sie klar ausdrücken was schief gegangen ist. Außerdem sollten Testnamen das Verhalten des zu testenden Objekts im Kontext des Szenarios beschreiben indem es getestet wird.

 OO Style

image

TDD ist einer der beiden Techniken um Software möglichst wartbar zu gestalten, OOP ist die Andere. Allerdings weiß eder OOP-Entwickler, dass man bei dieser Art der Programmierung auch einiges falsch machen. Wie erreicht man also, das Code so einfach wie möglich gleichzeitig aber auch so wartbar wir möglich geschrieben wird?   Die Autoren erwähnen zwei Heuristiken um solchen Code zu schreiben und strukturieren: “Separation of Concerns” und “Higher Levels of Abstractions”. Kommen diese beiden Prinzipien zur Anwendung entstehen so genannte “Ports and Adapters”-Architekturen, wobei der Domänencode von technologieabhängigen Code getrennt entwickelt wird.

Ports sind Interfaces die die Beziehungen mit der äußeren Welt beschreiben. Adapters sind Brücken zwischen der Applikationsdomäne und der technischen Domäne. Diese Brücken implementieren die Interfaces die in der Applikationsdomäne definiert werden. Sie sind daher ein Mapping zwischen Domänen- und technischen Objekten. Zwei weitere wichtige Richtlinien sind die Kapselung und Information Hiding. Diese beiden gilt es nicht zu verwechseln. Kapselung stellt sicher dass das Verhalten eines Objekts nur über dessen öffentliche Schnittstelle beeinträchtigt wird und Information Hiding verbirgt wie ein Objekt sein Funktionalität hinter dessen Abstraktion der öffentlichen Schnittstelle implementiert.

Was ich im Buch auch immer wieder lese, ist die Wichtigkeit der dynamischen Struktur des Objektgeflechts. Die Kommunikation von Objekten und die dabei entstehenden Kommunikationsmuster sind das “Um und Auf”. Peers nennt man dabei die Nachbarn eines Objekts mit denen es kommuniziert, also Nachrichten sendet und empfängt. Dabei ist wichtig, dass ein Objekt nicht zu viel Interna preisgibt, da sonst der Client eventuell zu viel Arbeit übernimmt und dabei verteiltes Verhalten entstehen würde. Dabei handelt es sich natürlich um einen absoluten Wartungsalbtraum. Folgende Arten von Peers eines Objekts werden im Buch beschrieben: Dependencies, Notifications und Adjustments.

Dependecies eines Objekts bieten Services ohne denen das Objekt nicht seinen Verantwortlichkeiten nachgehen kann. Notifications sind Peers die über den Status des Objekts bescheid wissen müssen (“fire and forget”). Bei Adjustments handelt es sich um Peers die das Verhalten eines Objekts verändern, um den breiteren Anforderungen des Systems zu genügen.

Auch Composites und das gegenteilige Konzept dazu “Context Independence” wird im Buch näher beschrieben. Dabei betonen die Autoren, dass das API eines zusammengesetzten Objekts nie komplizierter sein sollte, als die der einzelnen Komponenten. Context Independence stellt fest ob ein Objekt zu viel oder die falschen Dinge verbirgt. Diese Technik angewendet, ergibt explizite Abhängigkeiten zwischen Objekten die jedoch nicht in den Objekten definiert werden (Factory Objects).

OO Design erreichen

image

Wie setzt man nun all diese Techniken und Prinzipien im Code um? Wie kann ich dies mittels TDD erreichen? Prinzipiell kann man sagen, dass ein Client immer wissen möchte wovon ein Objekt abhängt und was es tut aber nicht wie es etwas tut. Bei TDD schreibt man zuerst den Test, d.h. wir müssen uns darüber Gedanken machen, was wir erreichen wollen bevor wie wir wissen wie wir es erreichen.

Wichtig dabei ist, dass man den richtigen Level of Abstraction findet. Wenn die Absicht des Unit-Tests unklar oder nicht eindeutig ist, dann kann es sein dass wir eventuelle Konzepte vermischen. Dazu dient das Konzept des Information Hiding, also was kann die äußere Welt sehen. Durch einschränken des Scopes eines Unit-Tests kann auch verhindert werden, dass der Test zu lange und daher unsauber wird.

Wie bereits erwähnt, liegt der Fokus des Designs darauf wie Objekte miteinander kollaborieren um die gewünschte Funktionalität zu erreichen. Klassenstruktur ist auch wichtig, jedoch sollte die Hauptaufmerksamkeit den Kommunikationsmustern (=Kommunikationsprotokoll) gewidmet werden.

“Ein Interface beschreibt ob zwei Objekte zusammenpassen während ein Protokoll beschreibt ob zwei Objekte miteinander arbeiten können”

Eine weiterer Grund für unklare Tests kann eventuell das Preisgeben von zu viel Information der Implementierung eines Objekts sein. Das bedeutet, dass vielleicht die Verantwortlichkeiten zwischen dem Objekt und dessen Peers neu überdacht bzw. re-balanciert  werden müssen.

Weitere Abschnitte des Buchs behandeln das Finden von Value Types und Objekten. Dabei kommen folgende Techniken für  Value Types zum Einsatz: Breaking out, Budding off und Bundling up. DieseTechnik kann auch auf echte Objekte angewendet werden. Dabei handelt es sich beim Budding Off um ein so genanntes “On-Demand” Design. D.h., Interfaces und deren Implementierung werden aus der Sicht und Bedarfs des Client entwickelt anstatt auf Verdacht Features einer Klasse zu erstellen. Das vermeidet das Preisgeben von zu viel Information über die eigentliche Implementierung und somit wird die implizierte Kopplung minimiert und der Code bleibt formbar. Interfaces bezeichnen dabei Rollen die ein Objekt einnehmen kann und die Nachrichten die sie empfangen bzw. senden können.

Im dritten Teil des Buchs werden kommen nun alle Techniken anhand eines Beispiel-Projekts zum Einsatz. Bei einigen Dingen bin ich mir noch nicht ganz sicher, jedoch hoffe ich, dass mir der Code dabei helfen wird, die beschriebenen Prinzipien und Techniken noch besser zu verstehen.

Thursday, August 12, 2010