PROGRAMMIEREN VON UNTEN NACH OBEN
OriginalJanuar 1993
(Dieser Aufsatz stammt aus der Einführung zuOn Lisp.)
Es ist ein althergebrachtes 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 noch leicht verständlich ist, wird sie zu einer Masse von Komplexität, die Fehler genauso leicht verbirgt wie eine Großstadt Flüchtlinge. Eine 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, desto mehr muss es aufgeteilt werden. Wie teilt man ein Programm auf? Der traditionelle Ansatz heißt Top-Down-Design: 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 wiederum vier eigene Unterprogramme haben", und so weiter. Dieser Prozess geht so lange weiter, bis das ganze Programm den richtigen Grad an Granularität hat - jeder Teil groß genug, um etwas Substanzielles zu tun, aber klein genug, um als eine einzige Einheit verstanden zu werden.
Erfahrene Lisp-Programmierer teilen ihre Programme anders auf. Zusätzlich zum Top-Down-Design folgen sie einem Prinzip, das man Bottom-Up-Design nennen könnte
- die Sprache an das Problem anpassen. In Lisp schreiben Sie Ihr Programm nicht nur nach unten zur Sprache hin, sondern bauen auch die Sprache nach oben zu Ihrem Programm auf. Während Sie ein Programm schreiben, denken Sie vielleicht "Ich wünschte, Lisp hätte so einen und so einen Operator." Also schreiben Sie ihn. Später stellen Sie fest, dass die Verwendung des neuen Operators den Entwurf eines anderen Teils des Programms vereinfachen würde, und so weiter. Sprache und Programm entwickeln sich gemeinsam. Wie an der Grenze zwischen zwei verfeindeten Staaten wird die Grenze zwischen Sprache und Programm immer wieder neu gezogen, bis sie schließlich an den Bergen und Flüssen, 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 einen Code, der klar, klein und effizient ist.
Es ist wichtig zu betonen, dass Bottom-Up-Design nicht bedeutet, dasselbe Programm in einer anderen Reihenfolge zu schreiben. Wenn Sie Bottom-Up arbeiten, landen Sie meist bei einem anderen Programm. Anstelle eines einzigen, monolithischen Programms erhalten Sie eine größere Sprache mit abstrakteren Operatoren und ein kleineres Programm, das in ihr geschrieben ist. Anstelle eines Sturzes erhalten Sie einen Bogen.
In typischem Code ist, wenn man die reine Buchhaltung herausnimmt, der verbleibende Teil viel kürzer; je höher Sie die Sprache aufbauen, desto weniger Strecke müssen Sie von oben nach unten zu ihr zurücklegen. Das bringt mehrere Vorteile:
Indem Bottom-Up-Design die Sprache mehr Arbeit verrichten lässt, entstehen Programme, die kleiner und beweglicher 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 Fehlerquellen. Wie Industriedesigner danach streben, die Zahl 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 verringern.
Bottom-Up-Design fördert die Wiederverwendung von Code. Wenn Sie zwei oder mehr Programme schreiben, werden viele der Hilfsprogramme, die Sie für das erste Programm geschrieben haben, auch in den nachfolgenden nützlich sein. Sobald Sie ein großes Substrat an Hilfsprogrammen haben, kann das Schreiben eines neuen Programms nur einen Bruchteil der Mühe erfordern, die es erfordert hätte, wenn Sie von Grund auf mit reinem Lisp hätten beginnen müssen.
Bottom-Up-Design macht Programme lesbarer.
Eine Instanz dieser Art von Abstraktion bittet den Leser, einen allgemeinen Operator zu verstehen; eine Instanz funktionaler Abstraktion bittet den Leser, ein spezielles Unterprogramm zu verstehen. [1]
Weil es einen dazu bringt, ständig nach Mustern in Ihrem Code Ausschau zu halten, hilft das Arbeiten von unten nach oben dabei, Ihre Ideen über den Entwurf Ihres Programms zu klären. Wenn zwei entfernte Komponenten eines Programms ähnlich aufgebaut sind, werden Sie dazu geführt, die Ähnlichkeit zu bemerken und das Programm vielleicht auf einfachere Weise umzugestalten.
Bottom-Up-Design ist in gewissem Maße auch in anderen Sprachen als Lisp möglich. Wann immer Sie Bibliotheksfunktionen sehen, findet Bottom-Up-Design statt. Lisp gibt Ihnen jedoch viel breitere Befugnisse in diesem Bereich, und das Erweitern der Sprache spielt in Lisp-Stil eine verhältnismäßig größere Rolle - so sehr, dass Lisp nicht nur eine andere Sprache, sondern eine ganz andere Art des Programmierens 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. Je größer die Gruppe, desto geringer die Produktivität der einzelnen Programmierer. Die Erfahrung des Lisp-Programmierens legt eine optimistischere Formulierung dieses Gesetzes nahe: Je kleiner die Gruppe, desto höher 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 tatsächlich gewinnen.
Neu: On Lisp kostenlos herunterladen.
[1] "Aber niemand kann das Programm lesen, ohne all Ihre neuen Hilfsprogramme zu verstehen." Um zu sehen, warum solche Aussagen meist falsch sind, siehe Abschnitt 4.8.