Читать книгу Prinzipien des Softwaredesigns - John Ousterhout - Страница 7

Оглавление

KAPITEL 1

Einführung (es geht immer um Komplexität)

Das Schreiben von Computersoftware gehört zu den uneingeschränkt kreativen Aktivitäten in der Geschichte der Menschheit. Programmierer und Programmiererinnen sind nicht an Beschränkungen wie die Gesetze der Physik gebunden – wir können spannende virtuelle Welten erschaffen, die so in der Realität nie möglich wären. Zum Programmieren benötigt man keine großen physischen Voraussetzungen und auch keine gute Koordination wie beim Ballett oder beim Basketball. Sie brauchen nur einen kreativen Kopf und die Fähigkeit, Ihre Gedanken zu ordnen. Können Sie sich ein System vorstellen, können Sie es vermutlich auch in einem Computerprogramm implementieren.

Die größte Einschränkung beim Schreiben von Software ist also unsere Fähigkeit, das zu erschaffende System zu verstehen. Wenn sich ein Programm weiterentwickelt und mehr Features notwendig sind, wird es komplizierter, und es gibt subtile Abhängigkeiten zwischen den Komponenten. Mit der Zeit summiert sich die Komplexität auf, und es wird beim Programmieren immer schwieriger, alle relevanten Faktoren beim Anpassen des Systems im Kopf zu behalten. Das verlangsamt das Entwickeln und führt zu Fehlern, was die Entwicklung noch mehr verlangsamt und noch teurer macht. Die Komplexität steigt mit zunehmender Lebenszeit jedes Programms ganz unvermeidlich. Je umfangreicher es ist und je mehr Personen daran arbeiten, desto schwieriger wird es, sie im Griff zu behalten.

Gute Entwicklungswerkzeuge können uns dabei helfen, mit der Komplexität umzugehen, und in den letzten Jahrzehnten entstanden viele großartige Tools. Aber es gibt eine Grenze bei dem, was wir allein mit Tools erreichen können. Wollen wir das Schreiben von Software leichter machen, damit wir mit weniger Aufwand leistungsfähigere Systeme bauen können, müssen wir Wege finden, die Software einfacher zu gestalten. Die Komplexität wird trotz all unserer Bemühungen mit der Zeit wachsen, aber ein einfacheres Design erlaubt uns, größere und leistungsfähigere Systeme zu bauen, bevor uns die Komplexität erschlägt.

Es gibt im Allgemeinen zwei Ansätze, Komplexität zu bekämpfen, und auf beide werden wir in diesem Buch eingehen. Der erste ist, die Komplexität zu reduzieren, indem wir den Code einfacher und offensichtlicher machen. So können wir beispielsweise Spezialfälle ausmerzen oder Namen konsistent einsetzen.

Der zweite Ansatz besteht darin, die Komplexität zu kapseln, sodass man in einem System programmieren kann, ohne mit der gesamten Komplexität auf einmal konfrontiert zu werden. Dieser Ansatz nennt sich modulares Design. Dabei wird ein Softwaresystem in Module aufgeteilt, wie zum Beispiel in einer objektorientierten Sprache in Klassen. Die Module sind so entworfen, dass sie voneinander relativ unabhängig sind, sodass man beim Programmieren mit einem Modul arbeiten kann, ohne die Details anderer Module verstehen zu müssen.

Weil Software so formbar ist, handelt es sich beim Softwaredesign um einen fortlaufenden Prozess, der den gesamten Lebenszyklus eines Softwaresystems begleitet – das unterscheidet das Softwaredesign vom Entwurf physischer Systeme, etwa von Gebäuden, Schiffen oder Brücken. Aber Softwaredesign wurde nicht immer so betrachtet. In der Anfangszeit der Programmierung konzentrierte sich das Design zunächst im Wesentlichen auf den Beginn eines Projekts – so wie in anderen Ingenieurdisziplinen. Der extremste Ansatz nennt sich Wasserfallmodell, bei dem ein Projekt in getrennte Phasen unterteilt wird, wie zum Beispiel Anforderungsdefinition, Design, Programmieren, Testen und Wartung. Im Wasserfallmodell wird jede Phase erst abgeschlossen, bevor die nächste beginnen kann – in vielen Fällen sind auch unterschiedliche Personen für die verschiedenen Phasen verantwortlich. Das gesamte System wird auf einmal in der initialen Designphase entworfen. Dann wird das Design am Ende der Phase eingefroren, und die folgenden Phasen haben die Aufgaben, dieses Design mit Leben zu füllen und zu implementieren.

Leider funktioniert das Wasserfallmodell für Software nur sehr selten. Softwaresysteme sind intrinsisch viel komplexer als physische Systeme – es ist nicht möglich, das Design eines großen Softwaresystems so gut darzustellen, dass alle seine Implikationen verstanden werden, bevor man etwas baut. Im Ergebnis wird das initiale Design viele Probleme haben. Diese werden erst sichtbar, wenn die Implementierung voranschreitet. Aber das Wasserfallmodell ist nicht dazu gedacht, an diesem Punkt noch große Designveränderungen zu ermöglichen (zum Beispiel können sich die Designteams schon einem anderen Projekt zugewandt haben). Daher versucht die Entwicklung, die Probleme zu umgehen, ohne das Gesamtdesign zu verändern. Das führt zu einer Komplexitätsexplosion.

Aus diesem Grund verfolgen die meisten aktuellen Softwareprojekte einen inkrementellen Ansatz, zum Beispiel die agile Entwicklung, bei der sich das initiale Design auf eine kleine Untermenge der Gesamtfunktionalität konzentriert. Diese Untermenge wird entworfen, implementiert und dann evaluiert. Probleme mit dem ursprünglichen Design werden erkannt und behoben, dann werden ein paar weitere Features entworfen, implementiert und evaluiert. Jede Iteration deckt Probleme des bestehenden Designs auf, die man behebt, bevor die nächsten Features entworfen werden. Indem wir das Design so immer weiter ausbauen, können wir Probleme mit dem initialen Design beheben, während das System noch klein ist – später hinzukommende Features profitieren von der Erfahrung, die während der Implementierung früherer Features gewonnen wurde, sodass weniger Probleme entstehen werden.

Der inkrementelle Ansatz funktioniert für Software, weil diese so flexibel ist, dass auch während der Implementierung noch signifikante Designänderungen vorgenommen werden können. Für physische Systeme sind größere Designänderungen hingegen viel schwieriger – so wäre es beispielsweise nicht praktikabel, die Anzahl der Pfeiler, die eine Brücke stützen, während der Bauphase anzupassen.

Inkrementelle Entwicklung bedeutet, dass Softwaredesign niemals fertig ist. Sie findet über den gesamten Lebenszyklus eines Systems hinweg statt, beim Entwickeln sollte man daher stets über Designthemen nachdenken. Inkrementelle Entwicklung heißt auch kontinuierliches Redesign. Das erste Design eines Systems oder einer Komponente ist so gut wie nie auch schon das beste – Erfahrung führt unvermeidlich zu besseren Wegen beim Umsetzen von Dingen. Als Softwareentwicklerin oder -entwickler sollten Sie immer nach Gelegenheiten Ausschau halten, das Design des Systems, an dem Sie arbeiten, zu verbessern, und Sie sollten einen Teil Ihrer Zeit für Verbesserungen am Design einplanen.

Wenn man während des Entwickelns von Software fortlaufend über Designthemen nachdenken soll und das Reduzieren der Komplexität das wichtigste Element des Softwaredesigns ist, sollte man beim Entwickeln immer auch über Komplexität nachdenken. Dieses Buch dreht sich darum, wie Sie das Thema Komplexität nutzen können, um sich beim Design von Software über deren gesamten Lebenszyklus leiten zu lassen.

Dieses Buch hat zwei größere Ziele. Das erste ist, die Natur der Komplexität von Software zu beschreiben: Was bedeutet »Komplexität«, warum ist sie wichtig, und wie können Sie erkennen, ob ein Programm unnötig komplex ist? Das zweite Ziel des Buchs ist herausfordernder: Ich präsentiere Techniken, die Sie während des Softwareentwicklungsprozesses nutzen können, um die Komplexität zu minimieren. Leider gibt es kein einfaches Rezept, das immer ein tolles Softwaredesign garantiert. Stattdessen werde ich eine Reihe von High-Level-Konzepten vorstellen, die sich philosophischen Aussagen bereits stark annähern, wie zum Beispiel: »Klassen sollten tief sein.« oder: »Definieren Sie die Existenz von Fehlern weg.« Diese Konzepte werden nicht sofort das beste Design aufzeigen, aber Sie können sie einsetzen, um Designalternativen zu vergleichen und um hierbei Ihren Gestaltungsspielraum zu erkunden.

Wie Sie dieses Buch einsetzen

Viele der hier beschriebenen Designprinzipien sind mehr oder weniger abstrakt, daher werden sie sich nur schwer würdigen lassen, wenn man sich keinen echten Code dazu anschaut. Es war schwierig, Beispiele zu finden, die klein genug waren, um sie in dieses Buch aufnehmen zu können, aber gleichzeitig groß genug, um Probleme an realen Systemen zu demonstrieren (wenn Sie gute Beispiele kennen, schicken Sie sie mir bitte zu). Daher kann es sein, dass dieses Buch allein nicht ausreicht, um zu lernen, wie Sie die Prinzipien anwenden.

Am besten nutzen Sie das Buch zusammen mit Code Reviews. Lesen Sie den Code anderer und denken Sie darüber nach, ob er den hier vorgestellten Konzepten entspricht und wie das mit der Komplexität des Codes zusammenhängt. Es ist einfacher, Designprobleme im Code von anderen Entwicklerinnen und Entwicklern zu finden als in eigenem Code. Sie können die hier beschriebenen Warnzeichen nutzen, um Probleme zu erkennen und Ideen für Verbesserungen zu entwickeln. Durch das Begutachten von Code werden Sie zudem neue Designansätze und Programmiertechniken kennenlernen.

Einer der besten Wege zum Verbessern Ihrer Designfähigkeiten ist, ein gutes Gespür für Warnzeichen zu entwickeln – Anzeichen, dass ein Stück Code vermutlich komplizierter ist, als es sein müsste. Im Verlauf des Buchs werde ich Warnzeichen (durch das Skorpion-Icon hervorgehoben) vorstellen, die auf Probleme hinweisen, die mit den jeweiligen Designaspekten verbunden sind – die wichtigsten habe ich noch einmal am Ende des Buchs zusammengefasst. Sie können sie beim Programmieren nutzen: Sehen Sie ein Warnzeichen, nehmen Sie sich etwas Zeit und suchen Sie nach einem alternativen Design, das das Problem behebt. Verfolgen Sie diesen Ansatz das erste Mal, werden Sie eventuell eine Reihe von Designalternativen prüfen müssen, bevor Sie die finden, die das Warnzeichen verschwinden lässt. Aber geben Sie nicht zu schnell auf: Je mehr Alternativen Sie ausprobieren, ehe Sie das Problem beheben, desto mehr werden Sie lernen. Mit der Zeit werden Sie feststellen, dass Ihr Code immer weniger Warnzeichen enthält und Ihre Designs immer klarer werden. Mit zunehmender Erfahrung werden Sie auch auf andere Warnzeichen aufmerksam, die Ihnen dabei helfen werden, Designprobleme zu erkennen (und ich würde mich freuen, davon zu hören).

Wenn Sie die Ideen aus diesem Buch anwenden, ist es wichtig, mit Augenmaß und besonnen vorzugehen. Jede Regel hat ihre Ausnahmen, und jedes Prinzip hat seine Grenzen. Treiben Sie eine Designidee ins Extreme, wird Sie das nicht dahin bringen, wohin Sie möchten. Schönes Design spiegelt eine Balance zwischen konkurrierenden Ideen und Ansätzen wider. Einige Kapitel enthalten Abschnitte, die »Wann Sie zu weit gehen« heißen und in denen beschrieben ist, wie Sie erkennen, dass Sie es mit einer guten Sache zu gut meinen.

Annähernd alle Beispiele in diesem Buch nutzen Java oder C++, und ein Großteil der Diskussion dreht sich um das Design von Klassen in einer objektorientierten Sprache. Aber die Ideen passen auch in anderen Domänen. Fast alle Ideen beziehen sich auf Methoden, die auch auf Funktionen in einer Sprache ohne Objektorientierung angewendet werden können, wie zum Beispiel in C. Die Designideen passen auch zu Modulen, die gerade keine Klassen sind, wie zum Beispiel Subsysteme oder Netzwerkservices.

Mit diesen Gedanken im Hinterkopf wollen wir uns nun genauer anschauen, was Komplexität verursacht und wie man Softwaresysteme einfacher gestaltet.

Prinzipien des Softwaredesigns

Подняться наверх