Sonntag, 17. Februar 2008

Wo war doch nochmal diese Klasse?

Was meinen Schreibtisch betrifft, so bin ich nicht einer von denen, die alles sofort sorgfältig abheften. Vielmehr bevorzuge ich eins dieser sehr persönlichen Ablagesysteme, die aus zwei bis drei Stapeln Papier bestehen. Diese wirken wie ein Cache. Alles was aktuell oder häufig gebraucht ist, befindet sich oben griffbereit. Alles was sich nach unten hin absetzt, ist offensichtlich lange nicht gebraucht worden und kann alle paar Monate entsorgt werden. Dies ist für die persönliche Ablage sehr effektiv.

Wenn wir aber ein Softwaresystem betrachten, so gibt es zwei entscheidende Unterschiede. Erstens arbeiten gewöhnlich mehrere Leute daran, die mit auf den Charakter Einzelner zugeschnittenen Ablagesystemen überwiegend nichts anfangen können. Zweitens sind selten angefaßte Quellen nicht notwendigerweise überflüssig. Gerade Kernkomponenten eines Systems werden oft nur sehr selten angefaßt, da sie im Gutfall so ausgereift sind, daß sich jeder blind darauf verläßt, und andernfalls traut sich keiner mehr, da etwas zu verändern.

Spätestens, wenn wir mehr Zeit mit Suchen verbringen als mit Kodieren, ist es Zeit darüber nachzudenken, wie man Ordnung in die Softwarequellen bringt.

Im Prinzip gibt es zwei Möglichkeiten Ordnung in die Softwarequellen zu bringen:

  • Namensgebung

  • Modulbildung

Namensgebung

In früheren Zeiten gab es für große Softwareprojekte Namenskonventionen, die es erlaubten, trotz meist nur kurzer Namen aus diesen direkt die Funktion des benannten Gegenstands zu erkennen. Ein bekanntes Beispiel sind die X$-Tabellen in Oracle, die den V$-Views zugrunde liegen. Eine Tabelle heißt x$kcbwait. An den ersten beiden Buchstaben (kc) läßt sich sofort erkennen, daß sie Teil des Cache-Layers ist.

Heute ist die Welt etwas einfacher geworden. Moderne Programmiersprachen unterstützen deutlich längere Namen und Namensräume. Namensräume erlauben es für einen Namen einen Kontext zu definieren, in dem er gebraucht wird. Derselbe Name kann dann in unterschiedlichen Kontexten verwendet werden ohne daß es zu Problemen kommt. Meist sind die Namensräume hierarchisch organisiert.

Module

Die andere Strukturierungsmöglichkeit ist die Aufteilung in Module. In der Entwicklungsumgebung werden dazu unterschiedliche Projekte angelegt. Daraus entstehen dann separate Dateien, z.B. jar-Dateien (Java) oder Assemblies (dotnet).

Ein Wildwuchs in der Zerlegung in Modulen ist der sichere Weg ins Desaster. Wenn hier kein klares Konzept vorliegt, wird die in der Überschrift zitierte Frage zum Leitthema der Entwicklung.

Die Aufteilung in Module bietet sich an, wenn die gleiche Funktionalität an verschiedenen Stellen installiert werden soll, z.B. sowohl in einem Client-Programm wie in einer Webserveranwendung. Hier empfiehlt es sich, zuerst etwas wie ein UML-Deployment-Diagramm zu zeichnen.

Ein anderer Grund, einzelne Module zu bilden, ist es, geschlossene Funktionalität so zusammenzufassen, daß die Module in verschiedenen Versionen der Software unverändert übernommen werden kann, wenn sich die notwendigen Änderungen nur auf andere Module beziehen. Wenn bei der Planung der Softwareänderung darauf geachtet wird, welche Komponenten betroffen sind, kann man sich viel Arbeit bei dem Verzweigen der Konfigurationen sparen.

Gute Module zeichnen sich durch klar definierte Schnittstellen aus, die sie nach außen veröffentlichen. Nicht alle Programmiersprachen liefern hier eine gute Unterstützung.

Es ist extrem empfehlenswert, die Namensräume mit den Modulen zu synchronisieren. D.h. der Namensraum sollte klarstellen, in welchem Modul sich die Klasse befindet.

Java

In Java gibt es Packages. Diese bieten sowohl hierarchische Namensräume als auch einen Ansatz der Modularisierung. Der vom Compiler erzeugte Bytecode ist in einem Verzeichnisbaum abgelegt, der genau die Namenshierarchie der Packages entspricht. Die Quellen müssen nicht so geordnet sein. Dies ist eine prima Methode Verwirrung zu stiften. Gleichzeitig kann mit der Package-Struktur der Zugriff auf Klassen geregelt werden. Wenn kein Zugriffsrecht (nicht private, public oder protected) definiert ist, ist der Zugriff nur innerhalb des Packages erlaubt. Hierbei spielt die Hierarchie der Packages leider keine Rolle, sondern der gesamte Namensraumteil des Klassennamens muß übereinstimmen. Dafür kann aber auch von einem anderen jar aus zugegriffen werden, wenn dort der gleiche Namensraum existiert.

Damit eignet sich diese Zugriffsreglung kaum für die Unterstützung von Modulbildung. Die einzige sinnvolle Anwendung ist es, den Zugriff für eng gekoppelte Klassen zu regeln, wie sie z.B. in vielen der GoF-Patterns zu finden sind, wo eine Klasse des Musters mehr über einen Partner wissen muß, als man guten Gewissens public machen möchte.

Das OSGi-Framework geht diese Schwäche der Java-Programmiersprache an und ist dabei so erfolgreich, daß Ideen davon möglicherweise in eine der nächsten Versionen der Sprache wandern werden.

C#

In C# gibt es Namensräume die unabhängig von anderen Eigenschaften flexibel angelegt werden können. Für die Zugriffsreglung gibt es das Schlüsselwort internal. Die damit gekennzeichneten Elemente sind nur innerhalb der Assembly zu sehen. Insofern kann dies dazu genutzt werden, den Zugriff zwischen Assemblies einzuschränken, um zu einem klaren Interface zu kommen. Wenn man also den Verdacht hat, daß andere auf ein Element zugreifen wollen, daß man verändern möchte, so kann man es auf internal setzen und sieht beim nächsten Build, wer betroffen ist.

Fazit

Moderne Entwicklungsumgebungen haben meist sehr gute Werkzeuge, die es erlauben im Code zu navigieren. Daher scheint die Ordnung nicht ein so großen Problem zu sein. Es zeigt sich aber, daß ab einer gewissen Größe des Projekt diese Unterstützung plötzlich zusammenbricht. Hinzu kommen Dinge, die die Entwicklungsumgebungen nicht so gut in Griff haben wie z.B. Konfigurationsdateien (Spring, JEE-Deploymentdeskriptoren ...). Daher sollte von Anfang an darauf geachtet werden, daß man jede Klasse unmittelbar lokalisieren kann, wenn man nur den Explorer dazu zur Verfügung hat. Die dadurch gesparte Zeit kann den Unterschied zwischen Sein und Nichtsein ausmachen.

Keine Kommentare: