Programmierung von unten nach oben
1993
(Dieser Aufsatz stammt aus der Einleitung zuOn Lisp .)
Es ist ein seit langem bestehender Grundsatz 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 ebenso leicht verbirgt wie eine Großstadt Flüchtlinge. Solche Software wird schwer zu lesen, schwer zu testen und schwer zu debuggen sein.
Gemäß diesem Grundsatz muss ein großes Programm in Teile unterteilt werden, und je größer das Programm, desto mehr muss es unterteilt werden. Wie teilt man ein Programm auf? Der traditionelle Ansatz wird als Top-Down-Design bezeichnet: Man sagt: „Der Zweck des Programms ist es, diese sieben Dinge zu tun, also teile ich es in sieben Hauptunterprogramme auf. Das erste Unterprogramm muss diese vier Dinge tun, also wird es seinerseits vier eigene Unterprogramme haben“, und so weiter. Dieser Prozess wird fortgesetzt, bis das gesamte Programm die richtige Granularität hat – jeder Teil groß genug, um etwas Wesentliches zu tun, aber klein genug, um als einzelne Einheit verstanden zu werden.
Erfahrene Lisp-Programmierer teilen ihre Programme anders auf. Neben dem Top-Down-Design folgen sie einem Prinzip, das als Bottom-Up-Design bezeichnet werden könnte – die Sprache an das Problem anpassen. In Lisp schreiben Sie Ihr Programm nicht nur nach unten zur Sprache, sondern Sie bauen die Sprache auch nach oben zu Ihrem Programm auf. Während Sie ein Programm schreiben, denken Sie vielleicht: „Ich wünschte, Lisp hätte dies oder jenes Operator.“ Also gehen Sie hin und schreiben es. Danach stellen Sie fest, 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 gezeichnet, bis sie schließlich entlang der Berge und Flüsse, den natürlichen Grenzen Ihres Problems, zur Ruhe kommt. 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, erhalten Sie Code, der klar, klein und effizient ist.
Es ist erwähnenswert, dass Bottom-Up-Design nicht nur bedeutet, dasselbe Programm in einer anderen Reihenfolge zu schreiben. Wenn Sie Bottom-Up arbeiten, erhalten Sie normalerweise ein anderes Programm. Anstelle eines einzelnen, monolithischen Programms erhalten Sie eine größere Sprache mit abstrakteren Operatoren und ein kleineres Programm, das darin geschrieben ist. Anstelle eines Sturzes erhalten Sie einen Bogen.
In typischem Code ist das, was übrig bleibt, viel kürzer, sobald man die reinen Buchhaltungsanteile abstrahiert hat; je höher man die Sprache aufbaut, desto weniger Distanz muss man von oben nach unten zurücklegen. Dies bringt mehrere Vorteile mit sich:
-
Indem die Sprache mehr Arbeit leistet, liefert Bottom-Up-Design Programme, die kleiner und agiler sind. Ein kürzeres Programm muss nicht in so viele Komponenten unterteilt werden, und weniger Komponenten bedeuten Programme, die leichter zu lesen oder zu modifizieren sind. Weniger Komponenten bedeuten auch weniger Verbindungen zwischen den Komponenten und somit weniger Fehleranfälligkeit dort. So wie Industriedesigner bestrebt sind, die Anzahl der beweglichen Teile in einer Maschine zu reduzieren, nutzen 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 Sie zwei oder mehr Programme schreiben, werden viele der Dienstprogramme, die Sie für das erste Programm geschrieben haben, auch in den nachfolgenden nützlich sein. Sobald Sie ein großes Substrat von Dienstprogrammen erworben haben, kann das Schreiben eines neuen Programms nur einen Bruchteil der Anstrengung erfordern, die es erfordern würde, wenn Sie mit rohem Lisp beginnen müssten.
-
Bottom-Up-Design macht Programme leichter lesbar. Eine Instanz dieser Art von Abstraktion bittet den Leser, einen Allzweck-Operator zu verstehen; eine Instanz von funktionaler Abstraktion bittet den Leser, ein Spezialzweck-Unterprogramm zu verstehen. [1]
-
Da es Sie dazu bringt, immer nach Mustern in Ihrem Code Ausschau zu halten, hilft die Arbeit von unten nach oben, Ihre Ideen über das Design Ihres Programms zu klären. Wenn zwei entfernte Komponenten eines Programms eine ähnliche Form haben, werden Sie dazu gebracht, die Ähnlichkeit zu bemerken und das Programm vielleicht auf einfachere Weise neu zu gestalten.
Bottom-Up-Design ist bis zu einem gewissen Grad auch in anderen Sprachen als Lisp möglich. Immer wenn Sie Bibliotheksfunktionen sehen, findet Bottom-Up-Design statt. Lisp gibt Ihnen jedoch weitaus umfassendere 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, sondern eine ganz andere Art zu programmieren ist.
Es stimmt, dass dieser Entwicklungsstil besser für Programme geeignet ist, die von kleinen Gruppen geschrieben werden können. Gleichzeitig erweitert er jedoch die Grenzen dessen, was von einer kleinen Gruppe geleistet werden 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 Gruppengröße sinkt die Produktivität einzelner Programmierer. Die Erfahrung mit Lisp-Programmierung legt eine erfreulichere Formulierung dieses Gesetzes nahe: Mit abnehmender Gruppengröße steigt die Produktivität einzelner 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 direkt gewinnen.
Neu: Laden Sie On Lisp kostenlos herunter.
[1] „Aber niemand kann das Programm lesen, ohne alle Ihre neuen Dienstprogramme zu verstehen.“ Warum solche Aussagen normalerweise falsch sind, siehe Abschnitt 4.8.