Wir nutzen Cookies, um das allgemeine Benutzerelebnis zu verbessern. Mit der Nutzung unseres Wikis stimmst du der Nutzung von Cookies zu.

Programmieren mit System

Dieser Artikel gibt Tipps, wie man bei komplexen Projekten den Überblick behält und mit System programmiert.

Konzept auf Papier

Bevor man zu programmieren beginnt, sollte man einen Plan haben. Zehn Minuten, in denen man sich mit Bleistift und Papier hinsetzt und aufschreibt, was das Programm können soll und in welche Teile es gegliedert wird, sind gut investierte Zeit. Wenn man zu mehreren arbeitet, sollte man eine Tafel, ein Flipchart oder ein großes Stück Packpapier verwenden, um Eckpunkte und Aufgabenverteilung festzuhalten. Grundsätzlich gilt: Ein Bild sagt mehr als tausend Worte. Grafisch arbeiten mit Blöcken, Pfeilen, Symbolen für den Datenfluss und knappen Beschriftungen ist besser als lange ausformulierte Sätze.

ProjektSkizze.gif

Zerlegung in übersichtliche Module

Auch die Skripte selber sollten in übersichtliche und gut handhabbare Einheiten gegliedert sein. Faustregel für ein Einzelskript: Länge nicht mehr als eine Seite. Damit behält man einen Programmabschnitt auf einem Bildschirm komplett im Blick, kann die Abläufe darin überschauen und ihre Logik und Fehlerfreiheit erheblich leichter beurteilen als bei vollgepackten Skript mit vielen Verzweigungen, geschachtelten Schleifen und tief gestaffelten Logikoperationen.

Man kann viele einzelne Skripte in eine Figur packen, aber das wird schnell unübersichtlich. Besser ist es, mehrere Figuren zur Gliederung zu verwenden, weil man dann über die Figurenübersicht navigieren kann. Figuren, die beim Ablauf des Programms nicht angezeigt werden, kann man als Schaltflächen mit kurzen Bezeichnungen anlegen. Wenn man ein visueller Typ ist, kann man natürlich auch ein passendes vorgefertigtes Scratch-Kostüm verwenden oder selbst eins zeichnen.

Die benutzerdefinierten Blöcke bieten eine gute Möglichkeit zur Gliederung. Selbst wenn man eigentliche alles in einem einzelnen Skript zusammenfassen könnte, kann es sinnvoll sein, in drei bis vier Hauptabschnitte zu untergliedern und diese über eigene Blöcke aufzurufen. Der Leser des Programms – meist ist man das selbst einige Zeit später - freut sich, wenn er eine Art Inhaltsverzeichnis der einzelnen Abschnitte vorfindet.

Bei Scratch hat eine gute Gliederung einen weiteren praktischen Vorteil. Wenn man man in ein großes Skript etwas einfügen oder etwas daraus entfernen will, bewegt man leicht größere Gruppen von Blöcken und muss sehr konzentriert arbeiten, um alles wieder an der richtigen Stelle einzusetzen. Auch hier macht man sich das Leben mit Gliederung leichter.

Programmierung im Detail

Auch in der Detailprogrammierung kann man sich durch systematische Arbeitsweise das Leben leichter machen. Übersichtlichkeit geht hier vor Performance.

Logik verstehen

Bevor man zu programmieren beginnt, sollte man die Logik der Aufgabe wirklich verstanden haben. Dabei können folgende Fragen helfen:

  • Wann muss was geprüft werden?
  • In welcher Reihenfolge?
  • Welche Wertebereiche können auftreten?

Wenn die Logik wirklich durchdacht ist, erhält man meist kürzere, sicherere und schnellere Programme als bei einfachem Drauflosprogrammieren.

Sicher und übersichtlich programmieren

Manchmal ist die Versuchung groß, möglichst viel Effekt in möglichst wenigen Zeilen oder Blöcken unterzubringen. Zum Selbstzweck sollte das aber nicht werden. Skripte, die wie Denksportaufgaben aussehen, sind schwer auf Richtigkeit hin zu kontrollieren und fehleranfällig. Besser ist es, so zu programmieren, dass bei einem kurzen Blick auf das Programm klar wird, wie es funktioniert und dass es richtig ist.

Optimierung nach Test

Es ist oft nicht von vornherein erkennbar, welche Teile eines Programms zeitkritisch sind. Wenn man im Vorfeld zu sehr auf die Optimierung achtet und z. B. versucht, durch Fallunterscheidungen Schleifendurchläufe einzusparen, erhöht man die Komplexität unnötig. Besser ist es, zunächst sicher und einfach zu programmieren, dann zu testen und zu sehen, welche Abschnitte wirklich zeitkritisch sind und oft durchlaufen werden, und ganz gezielt diese Abschnitte zu verbessern.

Möglichkeiten der Programmiersprache nutzen

Ein guter Programmierer kennt seine Sprache und nutzt ihre Möglichkeiten. Wenn man anfängt oder von einer anderen Programmiersprache her kommt, neigt man manchmal dazu, sozusagen mit einem begrenzten Wortschatz zu arbeiten. Das kann dazu führen, dass man mit viel Umstand eine Sache realisiert, die durch einen bereits vorhandenen Block oder einen eingebauten Parameter ganz einfach darzustellen ist. Ein Beispiel:

Statt

(Element (Zufallszahl von (1) bis (Länge von [Auswahlliste v]) ) von [Auswahlliste v])

geht einfach

(Element (zufälliges v) von [Auswahlliste v])

Um sich hier zu verbessern, sollte man sich hin und wieder einmal eine Befehlsgruppe vollständig ansehen, um das eine oder andere Brauchbare zu finden. Scratch bietet darüber hinaus hervorragende Möglichkeiten, sich mit anderen zu vernetzen. Man kann sich Projekte anderer Scratcher zu ähnlichen Themen ansehen, im besten Fall sogar Teillösungen übernehmen (Dank in den Projektanmerkungen nicht vergessen!), hier im Wiki nachsehen oder auch im Forum ganz direkt seine Anforderung schildern. Es gibt kaum ein Problem, das nicht schon einmal jemand gehabt hat, und die meisten Scratcher helfen gerne.

Definition von Variablen

Mit einer geschickten und sprechenden Benennung von Variablen kann sich die Programmierung in vieler Hinsicht erleichtern. Man könnte seine Variablen zwar einfach a,b,c usw. benennen. Für ganz einfache Programme reicht das auch völlig. Aber im Verlauf eines Projekts hat man schnell –zig verschiedene Variablen und dann sollte man seinem Gedächtnis doch helfen, indem man sprechende Bezeichnungen wählt, bei denen man direkt erkennt, was die Variable enthalten soll. Ein Beispiel:

define Ergebnis = (Basis)^(Exponent)
falls <(Basis) > (0)> dann
setze [Ergebnis v] auf ([10 ^ v] von ((Exponent) * ([log v] von (Basis))))
sonst 
falls <(Exponent) = (0)> dann
setze [Ergebnis v] auf [1]
sonst 
setze [Ergebnis v] auf ([0] - ([10 ^ v] von ((Exponent) * ([log v] von ([-1]*(Basis)))))

Wiedererkennbare Variablen

Wenn man z. B. ein Datum abfragen will, kann man die Variablen Tag, Monat und Jahr benennen oder TT, MM, JJJJ. Mit der zweiten Variante hat man sogar noch einen Hinweis auf das benötigte Eingabeformat. Wenn möglich, sind ausgeschriebene Worte besser als Abkürzungen. Allerdings sollte man bei der Länge mit Augenmaß arbeiten, sonst werden die Blöcke später unübersichtlich, gerade bei Verkettungen mathematischer Operatoren oder bei Vergleichen.

Laufindizes

Bei den häufig vorkommenden Zählschleifen werden Laufindizes benötigt, häufig auch geschachtelt. Hier sollte man systematisch arbeiten, so dass man ein Ordnungsschema erkennt, also z. B. die Indizes von außen nach innen i,k,l benennen.

Lokale Variablen

Gerade bei Zählern, aber auch bei anderen Variablen sollte man, wo es möglich ist, die Variablen lokal definieren, also beschränkt auf das Objekt, in deren Skript sie benötigt werden. Der Vorteil daran ist, dass man sich die Variablen beim Einsetzen in das Skript nicht aus einem Gesamtverzeichnis sämtlicher Variablen heraussuchen muss, sondern nur die angezeigt bekommt, die für das aktuelle Skript gültig sind. Außerdem ist eine Figur mit solchen lokalen Variablen unabhängiger und leichter auf andere Projekte übertragbar.

LokaleVariable.gif

Variablenverzeichnis anlegen: Data Dictionary

Man sollte sich ein Variablenverzeichnis (Teil eines Data Dictionary) anlegen, in dem man die verwendeten Variablen und Listen in alphabetischer Reihenfolge aufführt, bei jeder mit zwei bis drei Worten die Verwendung beschreibt, evtl. auch den Datentyp anführt, selbst wenn der in Scratch keine große Rolle spielt, möglicherweise auch den Wertebereich. Bei späterer Änderung des Programms wird man darauf gern zurückgreifen.

Hier ein Beispiel für ein Variablenverzeichnis:

DataDic.gif

Definition von Parametern

Neben der systematischen Namensvergabe für Variablen kann es sich lohnen, einzelne Werte, die die Variablen annehmen können, systematisch zu vergeben. Ein typisches Beispiel sind Boolsche Variablen, wo man sich entscheiden sollten, ob man deutsch WAHR/FALSCH oder englisch TRUE/FALSE benennt, oder Schalterstellungen AN/AUS ON/OFF oder Statusvariablen wie INAKTIV/LAUFEND/FERTIG/UNTERBROCHEN. Hier ist es gut, sich selbst Regeln zu geben und sich konsequent daran zu halten, um beim Programmieren nicht immer wieder nachsehen zu müssen.

Klonen

Bei Spielsteinen und ähnlichem kann man Figuren klonen, d. h. nach einer vorgegebenen Grundfigur beim Aufruf des Programms entstehen lassen. Ein Vorteil dabei ist, dass man Programmänderungen nicht für jede einzelne Figur durchführen muss, sondern nur einmal in der Grundfigur.

Testen

Bei der Entwicklung eines Programms sollte man genug Zeit zum Testen einplanen. Und man sollte das Programm so organisieren, dass es sich auch leicht testen lässt.

Auf getrennte Testbarkeit der Module achten

Dazu gehört die getrennte Testbarkeit der Module. Wenn alles mit allem zusammenhängt, kann man ein Projekt nur als Ganzes testen und es kann manchmal sehr lange dauern, bis man alle wichtigen Zustände und Kombinationen durchlaufen hat. Außerdem weiß man bei einem Gesamttest oft nicht auf Anhieb, wo der Fehler herkommt. Deswegen sollte man das Projekt so gliedern, dass man Einzelteile unabhängig von den anderen testen kann, und erst dann alles zusammenbringen, wenn die Einzelteile korrekt arbeiten.

Testdaten generieren

Das Testen lässt sich beschleunigen, wenn man Systemzustände, die normalerweise selten erreicht werden, mit vorgebbaren Daten gezielt erzeugt. Entweder durch manuelle Eingabe an den entsprechenden Punkten oder durch Daten in bestimmten Figuren oder Listen.

Empfehlenswert ist es auch, das Abspeichern aller wesentlichen Daten in Listen so vorzubereiten, dass man es zu beliebigen Zeitpunkten im Programm aufrufen kann. Wenn sich dann beim Testen ein seltener Fehler zeigt, kann man den Systemzustand festhalten und gezielt und genau reproduzieren.

Testkopie anlegen

Um einem Fehler auf die Spur zu kommen, muss man manchmal in das Projekt eingreifen, z. B. um Haltepunkte anzulegen. Damit man das nicht alles hinterher wieder entfernen muss, kann es sinnvoll sein, diese Tests in einer Testkopie des Projekts durchzuführen. Besonders zu empfehlen ist das dann, wenn man das Projekt bereits veröffentlicht hat und die anderen Nutzer nicht stören will.

Aufräumen

Von Zeit zu Zeit sollte man sein Projekt aufräumen und und nicht mehr benötigte

  • Kostüme,
  • Hintergründe,
  • Figuren,
  • Nachrichten,
  • Variablen und
  • Skriptabschnitte

konsequent entfernen. Das Projekt wird dadurch übersichtlicher und leichter wartbar. Außerdem vermeidet man damit Nebeneffekte durch vergessene Programmelemente.

Beim Aufräumen der Nachrichten an alle bietet Scratch Hilfe an. Bei Rechtsklick auf eine Nachricht wird folgendes Auswahlmenü angezeigt.

SenderEinerNachricht.gif

Je nach Auswahl werden die Figuren durch grüne Umrandung markiert, die die Nachricht senden oder empfangen. Verwaiste Nachrichten können so leichter erkannt und gelöscht werden.

Beim Löschen nicht mehr benötigter Variablen braucht man nicht allzu vorsichtig zu sein. Wenn man beim Löschen übersehen hat, dass die Variable doch irgendwo gebraucht wird, passiert nicht viel. Scratch erzeugt die Variable beim Ablauf des Programms neu. Man sollte den Bogen allerdings nicht überspannen. Wenn man z. B. alle Variablen löscht und eine globale Variable von mehreren Figuren aufgerufen wird, kann es passieren, dass Scratch die Variable mehrmals lokal erzeugt. Weil dann kein Datenaustausch über diese Variablen mehr stattfinden kann, funktioniert das Programm nicht mehr.



Code zum Einbinden ins Forum:
[wiki=de:Programmieren mit System]Programmieren mit System[/wiki]