Um Algorithmen zu skizzieren, Ideen aufzuzeigen u.ä. eignet sich insbesondere Pseudocode. Zumindest für alles unterhalb der Klassenebene. Sind mehrere Klassen beteiligt, eignen sich grafische Notationen wie UML meist besser, aber ansonsten ist Pseudocode toll. Vor allem ist toll, dass man den Detailgrad beliebig variieren kann. Von fast kompilierfähig bis „abstrakte Codekunst“ ist so gut wie alles möglich. Außerdem bietet es die Möglichkeit, programmiersprachenunabhängig zu bleiben.
Für dieses Blog habe ich mich entschieden, meinen Pseudocode etwas zu „standardisieren“. Der Nachteil von Pseudocode ist ja, dass er überall anders aussieht, was der allgemeinen Verständlichkeit wieder etwas abträglich ist. Oberstes Ziel bei „meinem“ Pseudocode soll die Lesbarkeit sein. Schon bisher habe ich „pascalartigen Pseudocode“ verwendet, weil dieser relativ gut lesbar ist.
Folgendermaßen werde ich also ab jetzt meinen Pseudocode schreiben:
- Es gibt keine Trennung zwischen Interface- und Implementation-Abschnitten wie bei Delphi
- Klassen sind wie Methoden aufgebaut, d.h. sie haben einen separaten Teil für Variablen (und Konstanten, Invarianten, etc.)
- Sichtbarkeitsmodifizierer u.ä. stehen bei jedem Feld und jeder Methode und nicht nur einmal
- zu jedem
begin
gibts einend
und umgekehrt - das
end
wird mit dem block-spezifizierenden Schlüsselwort qualifiziert extends
undimplements
wie bei Java machen Vererbung lesbarerinherited
stattsuper
self
stattthis
method
stattprocedure
bzw.function
procedure
undfunction
definieren klassenunabhängige Prozeduren=
und:=
wie in Pascal- Doppelte Anführungszeichen für String, einfache für Chars
- Leere Parameterklammern werden bei Methoden immer angegeben und können nicht ausgelassen werden (wie bei Java, u.ä.)
and, or, xor
für logische und binär-Operatorencatch
stattexcept
,null
stattnil
und!=
statt<>
preconditions
-,postconditions
– undinvariants
-Abschnitte bieten die Möglichkeit für DBC. Für jemanden, der sowas noch nicht gesehen hat, ist das vermutlich lesbarer als requires und ensures, denke ichreturn
(wie in C/C++, Java, etc.) undresult
(wie in Delphi) gibt es beides, wobei return die Methode verlässt.- Das zusätzliche Einfügen von
new
vor dem Konstruktoraufruf soll auch Delphi-Unkundigen verdeutlichen, was hier passiert. if
-Anweisungen haben entweder einen Bock (mitbegin
), der ggf. durchelse if
undelse
aufgeteilt sein kann oder keinen Block, dann gibt es aber auch keine Blöcke fürelse
-Fälle. Eventuelle Mehrdeutigkeiten werden durch eindeutige Einrückungen unterschieden.switch
-Anweisung ähnlich wie in C++/Java/etc. Ohnebreak;
wäre es zwar besser lesbar, würde aber wohl Leute verwirren, die so eines erwarten, also mitbreak
incase
und er Möglichkeit des Fall-Through.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | function foobar(): Integer; begin return 42; end function; procedure blubb(); begin nop; end procedure; class Subclass extends Baseclass implements ISomeInterface, IAnotherInterface const private KONSTANTE = 21; var private FMyVariable: Integer = 42; private FMyString: String = ""; invariants // optional DBC FMyVariable > 0; begin public constructor create(AParameter: String); begin inherited Create; FMyString := AParameter; doSth(); end constructor; public method doSth(AParam: Integer = 42): Void; // ":Void" optional var a: Integer; b: Integer; p: pointer to Integer; arr: array of String; preconditions // optional DBC AParam > 0; postconditions // optional DBC self.FMyVariable > old self.FMyVariable; invariants // optional DBC p != null; begin a := readln().toInt(); b := readln().toInt(); // if mit Block if a > b then begin a := 42; showMessage("a"); else if a = 0 then b := 12; else showMessahe("im else-Zweig"); end if; // if ohne Block: if isThursday then bad(); else good(); end method; internal static virtual synchronized method foobar(); // ähnlich Java/C# begin switch(x) begin case 0..10: doSth(); break; case 11..20: doNothing(); break; default: assert(false); end switch; for each i in 0..100 do begin writeln(i); end for each; end method; public static method main(AArgs: array of string): Void; // Programmeinsprungspunkt var obj: Subclass; begin obj := new Subclass.create("Foobar"); try begin obj.doSth(); catch(e: ESomeException) writeln(e); finally obj.free(); end try; end method; end class; |
Das ist keine formale Definition und das soll sie auch gar nicht sein. Ziel ist ja immer noch, Pseudocode zu schreiben, also nur eine Idee zu skizzieren.