Programmieren: Der Prozess der Softwareentwicklung
Aus Wikibooks
Es wurde bereits erwähnt, dass sich die Softwareentwicklung in zehn Teilgebiete unterteilt. Wir werden uns jetzt einen Überblick über diese Vorgänge verschaffen, um somit die Programmierung richtig einordnen zu können. Später wird genauer auf das eigentliche Thema dieses Buches eingegangen, das Programmieren selbst.
Zu erwähnen ist, dass es sich bei dem hier vorgestellten Ansatz wohl um den verbreitesten Ansatz eines Software-Lebenszykluses handelt - nämlich den des Wasserfallmodelles. Neuere Modelle wie zum Beispiel der des extreme Programmings (XP) sehen keinen Softwareentwurf im Detail vor sondern folgen den Paradigmen des Prototypings. Beim extreme Programming (XP) werden sogenannte "User-Storys" (Ich möchte an der und der Stelle dies und jenes machen können) modular abgearbeitet und ausprogrammiert, um diese später in das Programm einzubinden.
[Bearbeiten] Softwareanforderungsanalyse
Im Schritt der Anforderungsanalyse müssen alle Anforderungen an die Software auf ihre Konsequenzen für das Programm hin untersucht werden. Das bedeutet konkret, dass zunächst die Aufgaben genau definiert werden, die die spätere Software für den Anwender erfüllen soll. Anschließend werden diese Aufgaben in einzelne Funktionen heruntergebrochen, die das Programm zu bieten hat. Das Ergebnis wird jeweils in einem der Kerndokumente in der Softwareentwicklung festgehalten: dem Lastenheft und dem Pflichtenheft.
[Bearbeiten] Lastenheft
Zunächst ist es wichtig, sich darüber klar zu werden, welche Aufgaben die Software genau erfüllen soll. Dieser Schritt fällt um so leichter, je genauer der Prozess bekannt ist, in dem die Software eingesetzt werden wird. Der Prozess umfasst menschliche und/oder maschinelle Aufgabenträger, die gemeinsam eine Reihe von Aufgaben erledigen und dabei Information, Daten und Dokumente austauschen können. Weil so ein Prozess und die Daten, die er verwendet, hoch komplex sein können (und dies selbst bei einfachen Aufgaben erstaunlich oft auch tatsächlich sind), hat sich eine eigene "Kunst" entwickelt, diese Prozesse und Strukturen logisch zu durchdringen und abzubilden: die Prozess- bzw. Datenmodellierung.
Wenn ein konkretes Modell der Aufgabe für die zukünftige Software bereits existiert, ist die Definition des Lastenhefts relativ einfach; in diesem Fall gilt es, denjenigen Bereich aus dem Modell abzugrenzen, den die Software abdecken soll, und - falls das Modell sie nicht schon mitliefert - die gewünschten Schnittstellen zu benennen. Falls es das Modell noch nicht gibt, lohnt es sich vielfach, erst einmal zumindest ein grobes Modell zu entwerfen, um eine unter allen Beteiligten bekannte und akzeptierte Basis für die weitere Definition der Software zu haben. Wenn Modellierung nicht angebracht oder gewünscht ist, sollte zumindest eine peinlich genaue verbale Beschreibung der Aufgaben und Schnittstellen verfasst und von allen Beteiligten abgesegnet werden. Es lohnt sich, etwas mehr Arbeit in eine möglichst präzise und eindeutige Formulierung zu investieren; dies zahlt sich später in Form von geringerem Aufwand für die Beseitigung von Fehlern aus, die durch Ungenauigkeiten und Mißverständnisse zustande kommen.
- caveat
- Auch wenn das Lastenheft eine allgemeine Beschreibung und in aller Regel in natürlichsprachlicher Form abgelegt wird, lohnt sich schon hier die Verwendung von formalen Modellen zur Vermeidung von Mißverständnissen. Die Erfahrung lehrt, dass der Auftraggeber nicht immer eine wirklich präzise Vorstellung von der Aufgabe der gewünschten Software hat. Gerade in diesen Fällen ist die penible Konkretisierung bis zur Eindeutigkeit wünschenswert, um wirtschaftliche Risiken unter Kontrolle zu halten. Nicht wenige Softwarehäuser sind schon an schwammig formulierten Großprojekten und endlosen Nachbesserungsforderungen des Auftraggebers wirtschaftlich zugrunde gegangen.
[Bearbeiten] Pflichtenheft
Wenn das Lastenheft fertig gestellt und vom Kunden abgenommen ist, besteht der nächste Schritt in der Verfeinerung des Lastenhefts zum Pflichtenheft, das auch als Fach- oder Fachfeinkonzept bekannt ist. Dabei werden die Aufgaben aus dem Lastenheft in einzelne Funktionen zerlegt und der dazugehörige Daten- und Kontrollfluss festgelegt. Für jede Funktion werden die ein- und ausgehenden Daten einzeln spezifiziert, Funktionen werden zu Softwarebausteinen zusammengefasst und die Kommunikation der Bausteine untereinander wird definiert. So entsteht eine komplette technische Spezifikation der geplanten Software. Für die Entwicklung haben sich zwei Ansätze etabliert:
[Bearbeiten] Top-Down-Ansatz
Der sogenannte Top-Down-Ansatz setzt an der größten Einheit - dem Programm - an und bricht es in die nächst kleineren Einheiten - z.B. Module - herunter. Diese werden wiederum weiter zerlegt, so dass nach und nach die einzelnen Klassen, Objekte, Funktionen etc. sichtbar werden, bis eine weitere Zerlegung nicht mehr möglich ist.
[Bearbeiten] Bottom-Up-Ansatz
Den umgekehrten Weg geht der Bottom-Up-Ansatz. Ausgehend von kleinsten Einheiten wie Funktionen und Datenobjekten synthetisiert er nach und nach größere Objekte, Klassen, Module oder Funktionsgruppen, bis sich das Bild schließlich zu einem großen Ganzen - dem Programm - zusammenfügt.
Es verwundert nicht, dass beide Ansätze für ein und das selbe Projekt in der Regel zu unterschiedlichen Ergebnissen führen, die jeweils spezifische Vorzüge und Nachteile bei der Realisierung mit sich bringen. Eine komplette Durchführung beider Ansätze ist in der Praxis meist zu aufwendig; die hohe Kunst des Systemarchitekten besteht darin, beide Ansätze im Blick zu behalten, ohne sie komplett ausformulieren zu müssen, und das Beste aus beiden Ansätzen zu kombinieren.
[Bearbeiten] Softwareentwurf
Nachdem man weiß, was das Programm leisten soll, wird eine Grundidee entwickelt, wie das eigentliche Programm später aussehen könnte.
Das Programmpaket wird in maximal unabhängige Komponenten zerlegt, welche unterschiedliche Teilprobleme kapseln (Entflechtung!).
Es werden Schichten (Layers) eingeführt, welche von der Peripherie über den Kern zum Backend führen. --> Mehrschichtarchitektur.
Vorhandene Bibliotheken in Fassaden packen --> Wrapper schreiben, die 1) nur den Teil der Bibliothek abbilden, der benötigt wird und 2) die Schnittstellen für die aktuelle Problematik aufbereiten.
[Bearbeiten] Softwaremodell
Insbesondere für größere Projekte hat sich eine sorgfältige Modellierung der geplanten Software bewährt. Zum einen bieten die Modelle eine übersichtliche Grundlage für Organisation, Durchführung und Controlling der Realisierung. Andererseits bieten moderne Software-Werkzeuge in der Regel die Möglichkeit, aus Modellen, die nach Methoden wie UML oder SOM erstellt wurden, weite Teile des Quellcodes automatisch zu generieren. Die Wahl des Modellierungsansatzes ist abhängig von der Art des Projekts, der geplanten Implementierungssprache, den Wünschen des Auftraggebers und anderen Faktoren. Allen Modellen gemein ist jedoch der Gewinn an Struktur, Übersicht und Kommunikationsgrundlage für die Realisierungsphase, der entscheidender Faktor bei der rechtzeitigen Identifikation von Schwächen und Fehlern des Konzepts sein kann.
[Bearbeiten] Softwareprogrammierung
Das Ergebnis dieser Arbeit wird ein Quelltext sein, der nach der Kompilierung ausführbar ist und das erwünschte Ergebnis liefert.
Strategien für wartbare, wiederverwertbare, konvergente Software:
1) Klare Strukturen sind wichtiger als Performance. Wartbarkeit steht fast immer im direkten Widerspruch zur Geschwindigkeit. Deshalb: genau überlegen wo (wenn überhaupt) auf Geschwindigkeit optimiert werden muss. Meistens ist eine Optimierung nach Fertigstellung der Beta-Version erheblich effektiver als wenn dies während des Entwicklungsprozesses geschieht.
2) Sprechende Variablennamen ersetzen 70 Prozent der Doku.
3) Die globalen, groben Zusammenhänge dokumentieren.
Punkte 2) und 3) sind in älterer Software leider fast immer vertauscht: Details dokumentieren die Abkürzungen, globale Zusammenhänge bleiben völlig unklar (siehe TFM-Pages).
4) Keine "Magic Numbers" programmieren:
for i=42 to 999 do ...
sondern:
for aktuelleZahl = ERGEBNIS_DER_SINNFRAGE to MAX_ARRAY_LENGTH do ...
5) Sprechende Variablennamen vergeben.
LPSTR lpstr = "Hans Wurst"; --> LPSTR lpstrBenutzername = "Hans Wurst";
(lpstr steht hier für long pointer string, solche Bezeichnungen finden sich häufig in Programmen die für Windows programmiert wurden. LPSTR ist der Datentyp, den man folglich nicht frei wählen kann.)
6) Verschachtelte Logik vermeiden:
if ( checkInvalid() || handler=0 || maxHandler > 5) exit 1;
--> if ( checkInvalid() ) exit 1; if ( maxHandler > 5 ) exit 1; handler=0; * * *
Moderne Compiler erzeugen da den gleichen Code. Ausserdem: Punkt 1) !
[Bearbeiten] Testen von Software
Sobald ein Quelltext vorliegt, der übersetzbar ist und prinzipiell läuft, muss er daraufhin untersucht werden, ob er das macht, was man sich ursprünglich vorgestellt hat, oder in welchen Situationen irgendetwas anderes geschieht als das Gewünschte. Das nennt man dann Testen.
Testen selber ist ein Teil der Softwarequalitätssicherung, und es gibt nicht nur eine Art von Testen, sondern unterschiedliche. Diese unterscheiden sich sowohl durch die Art des methodischen Vorgehens (oder die Abwesenheit jeglicher Methode), als auch durch die Ebene, auf der das Testen ansetzt, ganz zu Schweigen vom Aufwand den man betreibt. Man kann z.B. einzelne Module (Teile) einer Software einzeln testen, man kann die Software als Ganzes testen, man kann die Software nach der Integration mit anderer Software testen. Oder man macht etwas völlig anderes, oder eine Kombination aus mehreren Vorgehensweisen.
Man kann Tests manuell durchführen oder automatisieren (eine vorteilhafte Idee). Automatisierte Tests empfehlen sich immer dann, wenn neue Versionen getestet werden sollen und überprüft wird, ob eine bisher vorhandene (Teil)-Funktion weiterhin richtige Ergebnisse liefert. Man kann die zu testende Software als schwarze Kiste (black box) ansehen und so tun, als ob man von deren Innenleben nichts weiß oder als weiße Kiste (white box), deren Innenleben beim Test auch kontrolliert wird. Die möglichen Kombinationen sind vielfälltig.
Hobbyisten beschränken das Testen üblicherweise auf ein bisschen Herumprobieren. Macht die Software in der eigenen Umgebung, auf dem eigenen Rechner und der eigenen Betriebssystem-Version in etwa das, was sie soll, wird das Herumprobieren beendet, und die Software gilt als getestet.
Finden sich beim Testen Fehler, so geht es zurück zur Softwareprogrammierung. Dort muss der Quelltext daraufhin untersucht werden, warum der konkrete Sachverhalt schief gelaufen ist, um dann diesen Fehler zu korrigieren, damit er in dieser konkreten, aber auch in anderen, ähnlichen Situationen nicht erneut auftritt.
Tests können nie die völlige Abwesenheit von Fehlern beweisen. Sie zeigen nur, dass unter bestimmten Bedingungen die Software oder ein Teil der Software funktioniert oder nicht funktioniert. Dabei ist häufig schon die Definition von "funktioniert" unklar. Ist die gewünschte Funktion im Anforderungsprofil der Software nur ungenau oder nicht beschrieben, so lässt sich das Verhalten einer Software durchaus unterschiedlich deuten.
Testen alleine stellt für eine professionelle Software keine ausreichende Form der Qualitätssicherung dar. Man kann Qualität nicht in eine Software "hineintesten". Der grundsätzliche Entwurf der Software beeinflusst deren Qualität und Wartbarkeit maßgeblich.
Eine wartbare Software, welche Fehler enthält, ist besser als eine Quick-And-Dirty-Version, die zu funktionieren scheint!
... denn fast jede Software wird irgendwann verbessert oder weiter entwickelt. Mit der Quick-And-Dirty-Version ist das nicht möglich. Der erste Fall (strukturierte Software) konvergiert mit der Zeit zu einer stabilen, ausgereiften Software, der zweite Fall (Q&D) wird im Laufe der Weiterentwicklung immer schwerer zu warten und fehleranfälliger.
Es empfiehlt sich, Test von den Personen durchführen zu lassen, für die das Programm bestimmt ist. Darüberhinaus ist ein Test durch einen DAU (dümmster anzunehmender User) sinnvoll, da diese Person Eingabe- und Bedienmöglichkeiten des Programms entwickelt, die einem Programmierer im allgemeinen nicht einfallen.
[Bearbeiten] Warten von Software
Das Warten von Software ist die Modifikation bestehender Software. Dies kann aus folgenden Teilaufgaben bestehen (teilweise nicht überschneidungsfrei):
- Fehlerentfernung
- Einbau neuer Features aufgrund von neuen Anforderungen
- Modifikation bestehender Features aufgrund von geänderten Anforderungen
- Tuning der Performance
- Refaktorierung (Umbau, Aufräumen) zur Verbesserung der Lesbarkeit und Wartbarkeit
- Anpassen an eine neue Umgebung (z. B. wenn die Software auf eine neue Betriebssystemversion übertragen werden muss.)
Bei all diesen Aufgaben muss natürlich sichergestellt sein, dass der Code nach wie vor fehlerfrei und erweiterbar ist. Bei großen Softwareprojekten ist dies aufgrund der Komplexität keine Selbstverständlichkeit. Daher sind für einen vernünftigen, weit blickendes Änderungsmanagement andere Phasen der Softwareentwicklung hierin enthalten.
[Bearbeiten] Konfigurationsmanagement
Das Konfigurationsmanagement (software configuration management) heißt nicht, Eigenschaftswerte, die zur Laufzeit veränderbar sind, zu ändern. Es bedeutet vielmehr, so genannte Konfigurationen zu verwalten. Eine Konfiguration ist eine Zusammenstellung von versionierten Objekten. Hat man beispielsweise eine Datei main.c und eine Datei function.c beide in der Version 1.0 für die Software-Version 0.9, dann kann dies eine Konfiguration sein, die zum Kompilieren der Software notwendig ist. Will man dann eine Version 0.9.5 der Software konfigurieren, in der beispielsweise ein Fehler entfernt worden ist, dann könnte man beispielsweise die an der Datei functions.c etwas ändern, hätte dann von dieser Version eine Version 1.1, die dann in die Konfiguration 0.9.5 einfließen würde. Es gibt Systeme, wie z. B. CVS oder subversion, mit denen man solche Aufgaben erledigen kann. Dort kann man auch detailliertere Informationen zu diesem Thema finden.
[Bearbeiten] Organisatorische Aspekte bei der Softwareentwicklung
Größere Software-Projekte erfordern Teamwork und ein geregeltes Management, da mehrere Programmierer gleichzeitig daran arbeiten.
[Bearbeiten] Der Vorgang der Erstellung von Software
Das Erstellen von Software ist im Allgemeinen das Eintippen von Quellcode (Basic, C, C++ etc.) unter Einbeziehung/Referenzierung anderer Funktionen oder Objekte. Um hieraus ein laufendes Programm zu erhalten, hängt es davon ab, welche Art von Sprache man verwendet:
- Wenn man eine interpretierte Sprache verwendet, reicht es aus, in der Entwicklungsumgebung auf so etwas wie einen "Start"-Knopf zu drücken, der dann die Software zur Ausführung bringt.
- Wenn man eine Compilersprache verwendet, sind im Allgemeinen noch folgende Schritte notwendig, um eine Software zur Ausführung zu bringen: Kompilieren (Erstellen von Maschinencode), Linken (Zusammenführen einzelner Teilmodule) und Ausführen
[Bearbeiten] Software-Werkzeuge und Verfahrensweisen bei der Softwareentwicklung
Bei der Erstellung von Software kann man als Entwicklungsumgebung prinzipiell einen graphischen Editor oder einen Text-Editor verwenden.
- Entwicklung mittels graphischem Editor:
Die Softwareentwicklung mittels graphischem Editor erfolgt das Erstellen der ausführenden Elemente über Graphik. Dies kann im Fall von Web-Anwendungen z.B. in Microsoft FrontPage oder Macromedia Dreamweaver geschehen. Zunächst erstellt hier der Entwickler beispielsweise ein Formular mit dem graphischen Designer. Auf dieses Formular werden dann die einzelnen Steuerelemente (Textbox, ComboBox, DataGrid, usw.) per Drag & Drop gezogen, anschließend positioniert und Eigenschaftswerte definiert bzw. an eine Datenbank durch die Eigenschaftswerte gebunden.
- Entwicklung mittels Text-Editor:
Die Entwicklung von Software mittels Text-Editor geschieht durch direkte Eingabe von Anweisungen und Eigenschaftswerten. Hierunter werden hauptsächlich Schlüsselwörter, Variablen, Strukturen, Schleifen, Verzweigungen, Eigenschaftswerte, usw. verstanden. Diese Methode kann auch als Ergänzung zur oben stehenden graphischen Entwicklung geschehen. Soll z.B. einem Label welches die Eigenschaft "Text" besitzt der Begriff (Wert) Name zugewiesen werden, so geschieht dies in C# folgendermaßen:
this.LabelName.Text = "Name";
Auch bei der Entwicklung von Datenbanken kann eine Kombination aus graphischen und textuellen Eingaben verwendet werden. Datenbanken speichern Daten. Dies sind beispielsweise Konfigurationsdaten einer Anwendung oder Benutzerdaten. Sehr häufig verwendete Datenbanken sind (das frei verfügbare) MySQL, Microsoft Access, Microsoft SQL Server oder Oracle. Die Daten besitzen in Datenbanken immer eine Struktur. Diese Struktur kann man graphisch mit speziellen Werkzeugen sichtbar machen und speichern (CASE-Tools wie z. B. ErWin). Man kann die Struktur der Daten aber auch textuell mit Hilfe von SQL beschreiben, wenn es sich um eine relationale Datenbank handelt.
[Bearbeiten] Softwarequalitätssicherung
Software-Qualitätssicherung (SQS oder einfach QS) besteht aus Aktivitäten, die den gesamten Lebenszyklus einer Software begleiten sollten. "Sollten", da in der Praxis ökonomische (keine Zeit, kein Geld), aber auch technische Gründe (für einige Probleme der QS gibt es nicht mal theoretische Lösungen) dazu führen, dass eine systematische QS nicht oder nur teilweise erfolgt.
Eine systematische QS nennt man auch ein (Software-)Qualitäts-Sicherungssystem. Die Aktivitäten, aus denen ein QS-System besteht, sind vielfältig und nicht auf das Programmieren beschränkt. Ein solches System enthält z.B. auch organisatorische Regelungen oder die Verpflichtung, vorgegebene technische Mittel wie ein Versions-Kontrollsystem oder gewisse Testwerkzeuge zu nutzen. Allgemein hängen die Aktivitäten auch vom gewählten Entwicklungsprozess, von den Lehrbuchmeinungen denen man folgt, persönlichen Vorlieben, Kundenvorgaben, gesetzlichen Vorgaben (Behörden-Software, militärische Software), usw. ab.
Von hunderten von möglichen QS-Aktivitäten seien hier nur die folgenden als Beispiel genannt:
- Unit-Tests: Programmierer schreiben für jeden Teil der Software parallel ein Testprogramm, das die grundlegende Funktion des Software-Teils (nicht des Gesamtsystems) automatisch und isoliert testet. Wird ein Fehler gefunden, schreibt man zunächst einen Unit Test, der diesen Fehler nachweist; anschließend behebt man den Fehler.
- Systemtests: Eine separate (!) Testgruppe erstellt Testszenarien und führt diese durch.
- Code-Reviews: Nach dem Fertigstellen eines definierten Teils der Software (z.B. einer Funktion) wird der Quelltext systematisch von anderen Programmierern durchgegangen und hinterfragt.
- Statistische Auswertung von Fehler-Reports: Auffällige Teile einer Software werden dann einer "Spezialbehandlung" (z.B. weitere Code-Reviews, Neuentwicklung) unterzogen.
Mit diesen Maßnahmen kann natürlich das Auftreten von Fehlern nicht vollständig verhindert werden; sie helfen aber, Fehler zu vermeiden und zu finden bzw. sicherzustellen, dass einmal korrekte Code-Teile auch korrekt bleiben. Je größer und komplexer das Projekt ist, desto wichtiger ist die Qualitätssicherung; andernfalls droht das vollständige Scheitern.
Aber jetzt einmal eine gute Nachricht: Zum Erlernen einer Programmiersprache benötigen Sie keine QS-Kenntnisse. Das geht ohne. Auch wenn Sie mit Ihrem privaten Hobbyprojekt oder Übungsprojekt beginnen, hängt ihre QS einzig und alleine von Ihnen ab. Sollten sich die ersten richtigen Benutzer für Ihre Software einstellen, dann wird es auch für Sie ernst (und dann werden Sie es eventuell bedauern, nicht etwas früher mit ein paar QS-Maßnahmen begonnen zu haben).

