Loading...

PROGRAMMIEREN VON UNTEN NACH OBEN

Original

January 1993

(Dieser Essay stammt aus der Einleitung zuOn Lisp.)

Es ist ein langjähriges Prinzip des Programmierstils, dass die funktionalen Elemente eines Programms nicht zu groß sein sollten. Wenn eine Komponente eines Programms über das Stadium hinauswächst, in dem sie leicht verständlich ist, wird sie zu einer Masse von Komplexität, die Fehler genauso leicht verbirgt wie eine Großstadt Flüchtige. Solche Software wird schwer zu lesen, schwer zu testen und schwer zu debuggen sein.

In Übereinstimmung mit diesem Prinzip muss ein großes Programm in Teile aufgeteilt werden, und je größer das Programm ist, desto mehr muss es aufgeteilt werden. Wie teilt man ein Programm auf? Der traditionelle Ansatz ist der Top-Down-Entwurf: Man sagt "der Zweck des Programms ist es, diese sieben Dinge zu tun, also teile ich es in sieben große Subroutinen auf. Die erste Subroutine muss diese vier Dinge tun, also wird sie wiederum vier eigene Subroutinen haben", und so weiter. Dieser Prozess wird fortgesetzt, bis das gesamte Programm die richtige Ebene der Granularität erreicht hat - jeder Teil ist groß genug, um etwas Wesentliches zu tun, aber klein genug, um als eine Einheit verstanden zu werden.

Erfahrene Lisp-Programmierer teilen ihre Programme anders auf. Neben dem Top-Down-Design folgen sie einem Prinzip, das man Bottom-Up-Design nennen könnte - die Sprache an das Problem anzupassen. In Lisp schreibt man sein Programm nicht nur in Richtung der Sprache, sondern baut die Sprache auch in Richtung seines Programms auf. Während man ein Programm schreibt, kann man denken "Ich wünschte, Lisp hätte so und so einen Operator". Also geht man und schreibt ihn. Danach erkennt man, dass die Verwendung des neuen Operators das Design eines anderen Teils des Programms vereinfachen würde, und so weiter. Sprache und Programm entwickeln sich gemeinsam. Wie die Grenze zwischen zwei kriegführenden Staaten, wird die Grenze zwischen Sprache und Programm gezogen und neu gezogen, bis sie schließlich an den Bergen und Flüssen ruht, den natürlichen Grenzen Ihres Problems. Am Ende wird Ihr Programm so aussehen, als wäre die Sprache für es entworfen worden. Und wenn Sprache und Programm gut zueinander passen, erhält man Code, der klar, klein und effizient ist.

Es ist erwähnenswert, dass Bottom-Up-Design nicht bedeutet, das gleiche Programm nur in einer anderen Reihenfolge zu schreiben. Wenn man von unten nach oben arbeitet, erhält man in der Regel ein anderes Programm. Anstelle eines einzigen, monolithischen Programms, erhält man eine größere Sprache mit abstrakteren Operatoren, und ein kleineres Programm, das in ihr geschrieben ist. Anstelle eines Sturzbogens, erhält man einen Bogen.

In typischem Code, sobald man die Teile abstrahiert, die nur Buchhaltung sind, ist das, was übrig bleibt, viel kürzer; je höher man die Sprache aufbaut, desto weniger Distanz muss man von oben nach unten zurücklegen. Dies bringt mehrere Vorteile:

Indem man die Sprache mehr Arbeit machen lässt, führt Bottom-Up-Design zu Programmen, die kleiner und agiler sind. Ein kürzeres Programm muss nicht in so viele Komponenten aufgeteilt werden, und weniger Komponenten bedeuten Programme, die leichter zu lesen oder zu ändern sind. Weniger Komponenten bedeuten auch weniger Verbindungen zwischen Komponenten, und damit weniger Chancen für Fehler dort. Wie Industriedesigner versuchen, die Anzahl der beweglichen Teile in einer Maschine zu reduzieren, verwenden erfahrene Lisp-Programmierer Bottom-Up-Design, um die Größe und Komplexität ihrer Programme zu reduzieren.

Bottom-Up-Design fördert die Wiederverwendung von Code. Wenn man zwei oder mehr Programme schreibt, werden viele der Dienstprogramme, die man für das erste Programm geschrieben hat, auch in den folgenden nützlich sein. Sobald man einen großen Unterbau an Dienstprogrammen erworben hat, kann das Schreiben eines neuen Programms nur einen Bruchteil des Aufwands erfordern, der erforderlich wäre, wenn man mit rohem Lisp beginnen müsste.

Bottom-Up-Design macht Programme leichter zu lesen.

Ein Beispiel für diese Art von Abstraktion fordert den Leser auf, einen universellen Operator zu verstehen; ein Beispiel für funktionale Abstraktion fordert den Leser auf, eine spezielle Subroutine zu verstehen. [1]

Weil es dazu führt, dass man immer nach Mustern in seinem Code Ausschau hält, hilft das Arbeiten von unten nach oben, die eigenen Ideen über das Design des Programms zu klären. Wenn zwei weit entfernte Komponenten eines Programms in ihrer Form ähnlich sind, wird man dazu geführt, die Ähnlichkeit zu bemerken und vielleicht das Programm auf eine einfachere Weise neu zu gestalten.

Bottom-Up-Design ist in gewissem Maße in anderen Sprachen als Lisp möglich. Immer wenn man Bibliotheksfunktionen sieht, findet Bottom-Up-Design statt. Lisp gibt einem jedoch viel größere Möglichkeiten in diesem Bereich, und die Erweiterung der Sprache spielt eine proportional größere Rolle im Lisp-Stil - so sehr, dass Lisp nicht nur eine andere Sprache ist, sondern eine ganz andere Art des Programmierens.

Es stimmt, dass dieser Entwicklungsstil besser geeignet ist für Programme, die von kleinen Gruppen geschrieben werden können. Gleichzeitig erweitert er jedoch die Grenzen dessen, was eine kleine Gruppe leisten kann. In The Mythical Man-Month, schlug Frederick Brooks vor, dass die Produktivität einer Gruppe von Programmierern nicht linear mit ihrer Größe wächst. Mit zunehmender Größe der Gruppe sinkt die Produktivität der einzelnen Programmierer. Die Erfahrung mit Lisp-Programmierung deutet auf eine fröhlichere Art hin, dieses Gesetz zu formulieren: Mit abnehmender Größe der Gruppe steigt die Produktivität der einzelnen Programmierer. Eine kleine Gruppe gewinnt, relativ gesehen, einfach, weil sie kleiner ist. Wenn eine kleine Gruppe auch die Techniken nutzt, die Lisp ermöglicht, kann sie völlig gewinnen.

Neu: Laden Sie On Lisp kostenlos herunter.

[1] "Aber niemand kann das Programm lesen, ohne all Ihre neuen Dienstprogramme zu verstehen." Um zu sehen, warum solche Aussagen in der Regel falsch sind, siehe Abschnitt 4.8.