22.08.2013 Aufrufe

Grundlagen der Informatik I “Programmierung”

Grundlagen der Informatik I “Programmierung”

Grundlagen der Informatik I “Programmierung”

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

4.2 Verifikation<br />

Beim Entwurf haben wir Vor- und Nachbedingungen an Routinen sowie Klasseninvarianten eingeführt, um<br />

“per Vertrag” die Verteilung <strong>der</strong> Aufgaben auf die einzelnen Module eines Softwaresystems klar zu regeln.<br />

Nach dem Entwurf <strong>der</strong> Systemarchitektur und <strong>der</strong> Verteilung <strong>der</strong> Einzelaufgaben ist es nun möglich, jedes<br />

Modul einzeln und unabhängig von den an<strong>der</strong>en zu implementieren. Statt einer globalen Sicht, die bisher<br />

erfor<strong>der</strong>lich war, ist es nun (endlich) möglich, lokal zu arbeiten, und auf konventionellere Programmierkonzepte<br />

zurückzugreifen, zu denen seit vielen Jahren Erfahrungen vorliegen und weiter entwickelt werden.<br />

Während dieser Implementierungsphase sind natürlich ganz an<strong>der</strong>e Qualitäten gefragt als beim Entwurf, in<br />

dem Erweiterbarkeit und Wie<strong>der</strong>verwendbarkeit die wichtigste Rolle spielten. Nun, da die Verträge erstellt<br />

sind, geht es darum, daß sie auch eingehalten werden. Ansonsten wäre die Modularisierung wertlos und eine<br />

Aufteilung <strong>der</strong> Verantwortung auf mehrere Softwareentwickler unmöglich, denn man könnte sich ja nicht<br />

darauf verlassen, daß die benutzten Dienstleitungen an<strong>der</strong>er Module auch so funktionieren, wie es vereinbart<br />

war. Zuverlässigkeit, also Korrektheit und Robustheit stehen bei <strong>der</strong> Implementierung von Klassen und ihren<br />

einzelnen Routinen im Vor<strong>der</strong>grund. Die Frage, die sich ein verantwortungsbewußter Programmierer nun<br />

stellen sollte, lautet: “Wie implementiere ich eine Routine, <strong>der</strong>en Zuverlässigkeit ich sicherstellen kann?”. Bei<br />

<strong>der</strong> Beantwortung dieser Frage sind zwei Aspekte zu berücksichtigen.<br />

Zum einen muß man natürlich wissen, welche Hilfsmittel überhaupt bei <strong>der</strong> Implementierung zur Verfügung<br />

stehen, also aus welchen Programmkonstrukten und elementaren Ausdrücken man den Anweisungsteil einer<br />

Routine aufbauen kann. Hier gibt es zwischen den meisten höheren Programmiersprachen nur geringfügige<br />

Unterschiede. Fast alle bieten Konstrukte für die in Abschnitt 1.3.2 angedeuteten Strukturierungskonzepte<br />

für Implementierungen an – also für eine Verfeinerung in eine Folge kleinerer Teilschritte, Fallunterscheidung,<br />

Wie<strong>der</strong>holung, Prozeduralisierung und ggf. auch Rekursion. 10 Da diese konzeptionell nicht so schwer zu verstehen<br />

sind und Probleme allenfalls bei <strong>der</strong> syntaktischen Beschreibungsform <strong>der</strong> Programmiersprache auftreten<br />

können, kann man sich die für eine Implementierung nötigen Grundkenntnisse relativ leicht aneignen.<br />

Zum zweiten aber – und das ist wesentlich schwieriger – muß man wissen, wie man diese Programmierkonstrukte<br />

einsetzt, um ein zuverlässiges Programm zu erhalten. Dabei läßt sich die Frage, wie man denn<br />

überhaupt eine lauffähige Routine erstellt, von <strong>der</strong> Frage nach <strong>der</strong> Korrektheit dieser Routine nicht trennen.<br />

Denn wer ein Programm entwickelt, <strong>der</strong> wird auch eine Vorstellung davon haben, warum dieses Programm<br />

denn so funktionieren soll, wie es vorgesehen ist. Wer also ein zuverlässiges Programm erstellen möchte, <strong>der</strong><br />

sollte prinzipiell auch in <strong>der</strong> Lage sein, die Korrektheit zu garantieren. Warum reichen hierfür die logischen<br />

Ausdrücke in Invarianten und Vor- und Nachbedingungen von Routinen nicht aus?<br />

Invarianten, Vorbedingungen und Nachbedingungen werden nur bei <strong>der</strong> Programmdurchführung kontrolliert,<br />

wenn die Durchführung an dieser Prüfstelle vorbeikommt. Ist die Prüfung negativ, so bedeutet dies, daß<br />

die Vorstellungen des Programmierers vom Programmablauf und <strong>der</strong> tatsächliche Programmablauf nicht übereinstimmen:<br />

ein Fehler wurde entdeckt. Ist aber die Prüfung in allen durchgeführten Fällen positiv, dann weiß<br />

man nur, daß in diesen speziellen Fällen kein Fehler gefunden wurde. Das sagt aber überhaupt nichts darüber<br />

aus, wie sich das Programm in allen an<strong>der</strong>en Fällen verhält, die in seinem späteren Einsatz vorkommen mögen.<br />

Man kann die Korrektheit eines Programms nicht mit Programmabläufen prüfen.<br />

Deshalb ist es nötig, die Korrektheit von Programmen völlig unabhängig von konkreten Eingabewerten beweisen<br />

(verifizieren) zu können, und dies in einer mathematisch präzisen Form zu tun. Prinzipiell ist es sogar<br />

wünschenswert, den Beweis in einem logischen Kalkül – also einer Erweiterung des in Abbildung 2.14 auf Seite<br />

49 vorgestellten Ableitungskalküls für die Prädikatenlogik – auszuführen, da dieser Beweis seinerseits durch<br />

10 Differenzen liegen nur in <strong>der</strong> syntaktischen Beschreibungsform dieser Konstrukte, dem Angebot mehrerer ähnlicher Konstrukte<br />

(z.B. while und for-Schleife in Pascal), und dem Umfang <strong>der</strong> vordefinierten Ausdrücke, die man nicht mehr selbst<br />

programmieren muß. Deshalb genügt es auch, diese Konzepte exemplarisch an <strong>der</strong> Sprache Eiffel zu besprechen. Hat man sie<br />

einmal verstanden, so kann man relativ leicht dasselbe Problem in einer an<strong>der</strong>en Programmiersprache implementieren, indem<br />

man im entsprechenden Manual nach <strong>der</strong> Syntax vergleichbarer Konstrukte sucht.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!