Freitag, 30. August 2013

Cobol in 1984



I was just browsing through the Oracle C/C++ precompiler files which come with the standard database installation. In the header directory there is a file named "sqlca.cob" which is obviously some kind of COBOL prototypes or however it is called in "their" world.


The reason why I want that to share this with you is the date stamp in the file's header (12/06/84).  Looking at the pace of how technology comes and goes these days it's just nice to see that apparently the host people are not impressed by that at all. Would be interesting if Clare is still with Oracle ;-)

Freitag, 23. August 2013

Fazit "Software Sanierung" von Sebastian Kübeck - Teil 1b, Entwurfsmuster

Der erste Teil des Buches "Software Sanierung" von Sebastian Kübeck beinhaltet eine umfangreiche Auswahl von Gang-Of-Four Entwurfsmustern inklusive guter Code-Beispiele. Die gewählten Patterns sind nach Ansicht des Autors "für das Sanieren" sinnvoll.

Nachdem ich auf meiner letzten Reise (nicht Urlaub) das Orginal-Gang-Of-Four Buch brav neben dem Bett liegen hatte, in der Hoffnung, dass sich Erkenntnisse daraus automatisch im Schlaf in Richtung meines Gehirns bewegen, habe ich hier die Chance ergriffen und die vorgestellten Muster im klassichen Schulprinzip durchgearbeitet. Dies sind meine Hefter-Notizen:

Abstrakte Fabrik
Klassisch wird ein Objekt mit "new" erzeugt. Eine Abstrakte Fabrik ist eine Klasse oder Interface, die mindestens eine Methode hat, die ein Objekt erzeugt.

Schablonenmethode/ Template Method
Ein Algorithmus wird in einer abstrakten Klasse definiert, wobei die Implementierung von Algorithmus-Details an Unterklassen ausgelagert wird. Zusätzlich kann die abstrakte Klasse auch noch leere Zwischenschrittsmethoden haben, die in Unterklassen optional implemenitert werden können.

Wert-Objekt
"Ein Wertobjekt ist dadurch gekennzeichnet, dass seine Identität von den Werten bestimmt wird aus denen es aufgebaut ist." Es hat keine eigene Identität. Im Buch wird noch darauf verwiesen, dass ein Kennzeichen von Java-Wertobjekten die Implementierung der Methoden "hashCode" und "equals" sind durch die der Programmierer einen Mechanismus zum Inhalts-Vergleich anbietet.
Ein Wertobjekt ist nach seiner Erstellung nicht mehr veränderbar (immutable).

Das Wert-Objekt ist nicht zu verwechseln mit dem auch in Javascript sehr populären "Data Transfer Objekt".

Null-Objekt
"Provide an object as a surrogate for the lack of an object of a given type. The Null Object Pattern provides intelligent do nothing behavior, hiding the details from its collaborators."
Im Buch wird ein Beispiel angeführt wo, abhängig von einem Konstruktor-Parameter, eine Ausgabe auf OutputStream-Objekt erfolgen soll - oder, wenn der Parameter "null" ist, anstelle dessen die Ausgabe über die Null-Objekt Implementierung von OutputStream.

Stellvertreter (Proxy)
Ein Objekt welches seine Methodenaufrufe an sein Orginal-Objekt weiterleitet. Stellvertreter und Orginal implementieren das gleiche Interface. Zusätzlich erfolgen aber noch weiter Aktionen im Stellvertreter die im Orginal nicht erfolgen.
Beispiel ist das Anklemmen von Logging. Dem Orginal-Objekt fehlt das Logging, der Stellvertreter besitzt die gleiche Signatur wie das Orginal, leitet die Methodenaufrufe auch an das Orginal weiter - aber loggt noch zusätzlich.

Adapter
Wie "Stellvertreter" allerdings underscheiden sich Methodensignatur von Orginal und Adapter Objekt. Dient als Verbindungsstück zwischen zwei verschiedenen Interfaces.

Beobachter
Häufig genutzt für Event-Handling in einer GUI. Die Beispiele im Buch haben aber eine andere Ausrichtung: Einem zu beobachtenten Objekt wird z.B. im Konstruktor ein Beobachter-Objekt übergeben. Dieser Beobachter könnte z.B. ein Logging implementieren.
An geeigneter Stelle werden dann im Orginal-Objekt die Beobachter-Methoden aufgerufen die dann z.B. zu einem Log-Eintrag führen.

Das Pattern ist praktisch für das testbar-machen von Log-Einträgen: Es gibt für das Beobachter-Objekt eine Produktiv-Implementierung, die einfach auf STDOUT schreibt und eine Test-Implementierung die sich zusätzlich das geloggte im Objekt merkt. Ein Unittest kann dann das "gemerkte" (z.B. eine Liste) vergleichen.
Dem zu beobachtenden Objekt (das mit der Fachlogik) wird dann der jeweilig passende Beobachter übergeben.

Fascade
Gang-of-Four Definition: "Biete eine einheitliche Schnittstelle zu einer Menge von Schnittstellen eines Subsystems. Die Fassadenklasse definiert eine abstrakte Schnittstelle, welche die Verwendung des Subsystems vereinfacht." (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: "Entwurfsmuster. Elemente wiederverwendbarer objektorientierter Software". Addison-Wesley. 1. Auflage 1996, S. 212)

Kommando
Kapsselung von Befehlen in ein Objekt. Im Buch wird ein Beispiel für einen einfachen Dateimanager gebracht. Die Hauptklasse (der FileManager) empfängt die Befehle als Arguments und ruft dann das entsprechende Kommando-Objekt auf. Die konkreten Aktionen zu einem Befehl sind im Objekt gekapselt.
Eine Erweiterung des Programmes ist einfach, es wird ein neues Kommando-Objekt angelegt (und getestet), dann wird der Hauptklasse noch das neue Objekt bekannt gegeben.

Strategie
Ein alter Bekannter im neuen Kleid, auch eine Anwendung des Abhängigkeits-Inversionsprinzips. Die Idee ist, Algorithmen die einer häufigen Änderung unterliegen via Interface zu implementieren.  und damit einfacher ausstauschbar zu machen.

Das Buch führt als Beispiel einen einfachen Passwort-Authenticator an. Die Passwort-Policy ist veränderlichen organisatorischen Regeln ausgesetzt - aus diesem Grund wird sie mittels Strategy-Pattern implementiert.
Konkret gibt es ein "PasswordPolicy" Interface welches nur die Methode "isSecure()" beinhaltet. Da die Frage "isSecure()" organisatorischen Änderungen unterworfen ist, wird die konkrete Implementierung von "PasswordPolicy" regelmäßig angepasst in dem eine neue Klasse geschrieben wird.
Die Authenticator-Hauptklasse bekommt im Konstruktor nur die jeweilig aktuelle Implementierung der Policy übergeben.

Mittwoch, 21. August 2013

Fazit "Software Sanierung" von Sebastian Kübeck - Teil 1a, Einführung und Design Prinzipien

Ohne konkrete Zahlen herauszukramen, stelle ich hier die Behauptung auf, dass ein Software Entwickler in seinem Berufsleben viel mehr mit existierendem Code arbeitet als dass er regelmäßig die berühmte "Grüne Wiese" bestellt.
 Dem Pragmatismus der Pragmatic Programmers folgend, versuche ich aus diesem Fakt eine Tugend zu machen, mit dem Ziel "Master Of Legacy Code" zu werden. Und ganz ehrlich, sich einen fremden, alten, nicht Unit-getesteten Code zu eigen machen - das ist doch im Grunde herausfordernder als immer nur ein neues Town House auf die planierte Fläche zu setzen.

Dieser Logik folgend stürzte ich mich neulich enthusiastisch auf das Buch "Software Sanierung" von Sebastian Kübeck, erschienen  2009 im mitp-Verlag. Um es vorweg zu nehmen: hiermit gestehe ich, dass in der Vergangenheit meine Unit-Tests häufig eher externe Tests waren.

Der erste Teil des Buches ist ein Crash-Kurs im benötigten Handwerkszeug:

  • Objektorientierung
  • Tests inklusive Abgrenzung der verschiedenen Test-Arten
  • Wichtige Design Patterns
  • Refactoring Patterns
  • Fehlerbehandlung (Exceptions)

Da dieses Blog mein öffentliches Gehirn sein soll, hier die Liste der Dinge, die ich als wichtig in diesem ersten Teil empfand:

Natürliche versus Künstliche Komplexität
Die natürliche Komplexität beschreibt letztendlich die Grund-Komplexität des implementierten Fachprozesses. "Der kleinstmögliche Umfang an Informationen, die notwendig sind um [ eine Problemstellung ] vollständig zu beschreiben, definiert die natürliche Komplexität des Problems."


Die natürliche Komplexität lässt sich nur verringern in dem man Features aus einer Anwendung wieder ausbaut und sie damit wieder vereinfacht.

Nun zur künstlichen Komplexität: "...der Ballast .., der nötig ist um Programme unter den gegebenen Rahmenbedingungen und mit den Kenntnissen der Programmierer zu realisieren."

Die künstliche Komplexität kann man direkt verringern, zum Beispiel durch gutes Design, Hochsprachen oder die Verwendung von Bibliotheken anstelle von eigenen Implementierungen. Ein guter Entwickler erhöht also bei einer Programmerweiterung die künstliche Komplexität nur um das wirklich notwendige Minimum - so weit zumindest die Theorie.

Sanieren statt Wegreißen
In der Einleitung werden ein paar gute Argumente für eine Sanierung bestehender Software angeführt, besonders wichtig finde ich diesen hier (in eigenen Worten):
Häufig ist die Software selbst die einzig verbliebene, aktuelle Spezifikation des Fachprozesses. Wissensträger sind teilweise nicht mehr verfügbar, die existierende Dokumentation ist lückenhaft. Allein das Programm beinhaltet das gesammelte Wissen der letzten x Jahre/ Jahrzehnte.

UML Klassendiagramme sind zwar schick aber...
...spiegeln nicht die Interaktion der Objekte wieder. Außerdem entsteht kein Programm aus einer Klassen/ Objekt-Beschreibung. Das scheint wohl noch aus der Zeit zu kommen, wo man Klassendiagramm gemalt hat und sich dann den Code per Knopfdruck generiert hat. 
Viel näher am Software-Entwicklungsprozess sind die Interaktionsdiagramme die erst später in die UML aufgenommen wurden. Das Nützlichste aus meiner Sicht ist das Kommunikationsdiagramm - letztendlich eine Formalisierung der Kästchen mit Pfeilen die man sowieso gern zur Visualisierung verwendet.

Interface-Aufteilungsprinzip
"Interfaces sollten nur so viele Methoden haben, wie für die Ausführung einer Aufgabe unbedingt nötig sind. Können zusätzliche Methoden zur Verfügung gestellt werden, sollte man das Interface aufteilen."

Liskov Substituitions-Prinzip
"If it looks like a duck, quacks like a duck, but needs batteries – you probably have the wrong abstraction" (Link)

Das Web ist voll mit Erklärungen dieses Prinzips.Grundsätzlich soll jede Erweiterung einer Klasse die Elternklasse vollständig ersetzen.

"Der Nachteil der Verletzung des Liskov-Substitutionsprinzips liegt in der Erwartungshaltung an eine Erweiterung einer Klasse. Da man dank der Polymorphie unter Umständen nur it der Elternklasse arbeitet, ohne zu wissen, dass man es eigentlich mit einer Ableitung zu tun hat, ist es äußerst unangenehm, wenn sich diese Klasse ganz anders verhält als die Elternklasse."

Für mich wird dieses Prinzip durch ein Beispiel am besten deutlich. Robert Martin hat dies sinngemäß einmal so erklärt: Mathematisch ist ein "Quadrat" ein "Rechteck". Man ist also versucht auch eine Klasse "Quadrat" von einer Basisklasse "Rechteck" abzuleiten.
Die Methoden "setX()"und "setY()" machen bei einem Rechteck durchaus Sinn - beim einem Quadrat allerdings nicht wirklich. Hier setzt der Aufruf einer Methode alle vier Seiten. Die Abstraktion "ein Quadrat ist ein Rechteck" passt hier also unter objektorientierter Betrachtungsweise schlecht.

Abhängigkeits-Inversionsprinzip
Auf diesem Prinzip bauen fast alle Refactorings des Buches auf. Es sagt aus, "dass Klassen möglichst nicht von konkreten Implementierungen anderer Klassen, sondern von deren Interfaces abhängig sein sollen".
In der Praxis bietet es sich an eine starre Kopplung z.B. an die JDBC-Klassen durch ein eigenes Interface aufzulösen, Die Produktionsimplementierung des Interfaces ist letztendlich ein Wrapper (ja, ich weiss, es heißt "Delegation") um die JDBC-Klassen.
Die Testimplementierung nutzt das gleiche Interface, emuliert aber Aktionen wie "getLastName()" mittels Hashmap.

Wenn nun der Ursprungsklasse während der Laufzeit eine andere Datenbank-Klasse mittels z.B. "setDatabase()" Methode "injiziert" wird, spricht man von "Dependency Injection".

Änderungsvektoren (einer Klasse)
Im Laufe der Zeit wird häufig eine Klasse durch verschiedene Änderungswünsche (Anforderungen) in verschiedene Richtungen getrieben. Diese verschiedenen Richtungen nennt man Änderungsvektoren.

Single-Responsibility Principle
Eine Klasse sollte nur einen dieser Vektoren implementieren. Also z.B. sich nur um Datenbankaktionen kümmern und keine Berechnungen durchführen.

Das Gleiche gilt für Methoden: checkAndStoreData() wird besser zu "checkData()" und "storeData()". Dieses Prinzip ist universell und gilt genauso für C Dateien und Funktionen.


Mittwoch, 14. August 2013

Why pressing buttons

This is the personal blog of me, a software developer who his enthusiastically digging all kinds of subjects related to software engineering. The posts are supposed to be my public memory since so many things are popping up every day - nobody can't seriously memorize that. At least not my average brain. Instead of scribbling my thoughts into some moleskin I share them here with you. Not as fancy but hey! - we're digital people, aren't we?

Since I'm native German the posts will be German and sometimes in English, depending on my mood and the topic.

The blog is named after the very recommendable album of a band called "Radio Burroughs". Check their bandcamp page.