Projekttage allgemein, und Schule als Staat

Wer Projekttage sagt, der spricht ein großes Wort gelassen aus – John Dewey, Georg Kerschensteiner, Reformpädagogik, alternative Unterrichstformen. Der Umgang mit dem ursprünglichen großen Projektgedanken ist gelassen geworden, gemeint ist in der Praxis, und das schon seit meiner eigenen Schulzeit: Wie kriegen wir die letzten Schultage herum?

Mit dieser Ausgangssituation kann ich mir keine Schuljahresendbeschäftigung vorstellen, die dem Projektgedanken genüge tut. Wenn die Ausgangssituation die wäre, dass man als Schule ein Projekt machen möchte – dann sollte man das zu einem Zeitpunkt im Schuljahr tun, zu dem genug Zeit zur Vor- und Nachbereitung ist, zur Einbindung in den Unterricht, und wo alle noch bei Kräften sind. Wenn das dann mal geklappt hat, im März etwa, dann kann man ja schauen, ob das auch unter den ungünstigeren Juli-Bedingungen geht. (Wie man die letzten Tage am besten herumkriegt, darauf habe ich jetzt auch keine Antwort. Darum geht es mir hier auch nicht.)

Ein solches Projekt heißt “Schule als Staat” und hat eine eigene (kleine) Seite bei Wikipedia. Das projekt läuft über mehrere Tage; die Schüler und Schülerinnen bauen dabei eine Art Infrastruktur und Verwaltung auf. Viel habe ich online nicht gefunden, die informativste Seite dazu nennt:

  • eine demokratische Verfassung
  • frei gegründete politische Parteien
  • ein direkt gewähltes Parlament mit Staatspräsident und Minister
  • Beamte und staatliche Institutionen (Zoll, Zentralbank, Wirtschaftskontrolldienst, Müllabfuhr)
  • eine Zentralbank mit einer eigenen Währung
  • viele Betriebe, die eigenständig wirtschaften (Bars, Restaurants, Werbefirmen, usw.)
  • Kulturbetriebe (Kunst, Theater, Sport, … )
  • Gesangswettbewerbe
  • Polizei, Richter und ein Standesamt
  • Presse (Zeitung, TV und Radio)

Bei uns kommt es aus Zeitgründen immer nur zu einer sehr kleinen Form dieses Projekts. Das ist sicher keine schlechtere Idee für die letzten Tage als ohne Staat Kuchen zu verkaufen, aber vom ursprünglichen Gedanken bleibt aus Zeitgründen nur wenig: Der Staat ist vorgegeben; die Verfassung und Gesetze ändern kann man nicht. (Eine Judikative und Exekutive gibt es. Nur eben keine Volksvertreter.) Die Aufgabe des Staats wird reduziert darauf, ein ungestörtes Wirtschaftsleben zu ermöglichen; dementsprechend heißen die Teilprojekte auch “Unternehmen”. Eine Art poll tax gibt es, aber keine Grund‑, Mehrwehrts‑, Einkommens‑, Vermögenssteuer.

Jeder Schüler und jede Schülerin erhält das gleiche Startkapital, was suggeriert, dass das in unserem realen Staat ebenso ist. Ressourcen von außen (von den Eltern gekaufte Elektrogeräte, zu Hause gebackene Kuchen) werden in den Wirtschaftskreislauf in der Schule gebracht, so unerschöpflich wie Bodenschätze und fossile Brennstoffe lange gesehen wurden. Arbeitslosigkeit ist kein Problem, weil man die paar Tage ja ohne Lohn auskommen kann, der ohnehin nicht nennenswert hoch ist.

Mehr geht in zwei Tagen auch nicht, und nicht in der letzten Schulwoche. Schöner wäre halt, wenn man ein richtiges Planspiel daraus machen könnte, oder nur ein Spiel. Dazu gehörten unterschiedliche Ausgangspositionen, vielleicht zwei konkurrierende Gesellschaftsentwürfe. Ein bisschen wie bei Junta, was der Kollege mal in einer Vielspielerversion einzurichten versucht hat – da zieht el presidente die Entwicklungshilfe ein und verteilt die nach Gutdünken. Also mehr LARP als Schule-als-Staat, oder ein Ressourcen-Management-Spiel. (Siehe auch: Nicht-rundenbasierte Spiele.) Dann würden die Teilnehmerinnen sich mehr Gedanken darüber machen, wie Systeme funktionieren, welche Rolle unterschiedliche Ausgangspositionen haben.

So ein Planspiel könnte man parallel zum Unterricht laufen lassen, und erst einmal mit einer kleineren Gruppe, und natürlich mitten im Schuljahr.

Ich war als junger Mann mal eine Weile Mitglied in einem Briefspiel-Verein. Spiele per Post, das war mal eine große Sache, vor dem Internet. Diplomacy etwa ist ein Briettspiel, das in Europa in der Zeit vor dem ersten Weltkrieg spielt, für das man sieben Spieler braucht und viel Zeit – die Spieler verbringen eine Viertelstunde mit Verhandlungen, auch privat geführten, am besten in der ganzen Wohnung verteilt, und schreiben danach ihre Züge auf einen Zettel (die vielleicht dem entsprechen mögen, was sie versprochen haben). Die Züge werden ausgeführt, und dann kommt die nächste Verhandlungsrunde. – So bietet sich Diplomacy besonders als Briefspiel an; alle zwei Wochen eine Zugabgabe, dazwischen stilvolle diplomatische Verhandlungen per Post.

In diesem Briefspiel-Verein liefen parallel viele Spiele, und die laufenden Ergebnisse wurden im regelmäßig erscheinenen Magazin veröffentlicht. Unter anderem gab es auch ein Meta-Spiel, eine Art Aktienhandel: Man handelte mit Aktien der Spieler in den anderen Spielen, also etwa der Diplomacy-Runde – schien da ein Spieler gut zu spielen, gingen dessen Aktienkurse hoch. Pädagogisch unmöglich, aber spieltechnisch reizvoll, wären Aktien auf Schulklassen. Am Anfang des Jahres gibt es für die Klassen 9a, 9b, 9c, 9d Aktien, und die Gewinnausschüttung hängt von den Halbjahresnoten oder der Menge an erteilten Ordnungsmaßnahmen ab. Geht natürlich nicht, ist aber gar nicht so weit entfernt von dem Punktesystem bei Hogwarts.

(So, ich mach jetzt erst mal weiter Urlaub.)

Gedanken beim Lesen eines Sprachlernbuchs von 1975

Spanisch in 30 Lektionen: Jede Lektion beginnt mit einem kurzen, dialogreichen spanischen Erzähltext, den ich mit meinen mageren, aber durchaus mal vorhanden gewesenen spanischen Grundkenntnissen zu verstehen versuche. Das ist anstrengend und befriedigend. Die banalsten Geschichten werden unglaublich spannend: Diese Anita! Was die immer erlebt! Das letzte Mal hat sie im Unterricht aufgepasst und wusste die richtige Antwort, und der Mitstudent nicht. Ich bin schon gespannt auf ihr nächstes Abenteuer, ganz stolz, diesem vorherigen zu folgen in der Lage gewesen zu sein.

Ich fühle mich dabei auch ein bisschen wie Indiana Jones, wenn er antike Tafeln übersetzt, so mit dem Finger den Zeichen folgend, pausierend, mal wieder zurück zum Anfang des Satzes, und nach und nach das ganze rekonstruieren. Ich kann vermutlich nur wenig schlechter Spanisch als Indy Aramäisch.

Können frühe geschriebene Geschichten anspruchslos sein, weil es abenteuerlich genug ist, überhaupt etwas lesen zu können? Früh in der Entwicklung einer Kultur oder in der eigenen Leseerfahrung? Kann man wohl bei Kulturen schlecht sagen, da geschriebene Erzählungen wohl lange einer kleinen, lesen könnenden Gruppe vorbehalten waren.

Eine kleine Programmieraufgabe…

Benutzeroberfläche Inselspiel

…die sich als aufwändiger und lehrreicher herausgestellt hat, als ich dachte.

Es begann damit, dass ein Kollege mir von dem Computerspiel erzählte, das ihn gerade beschäftigte: Als Schiffbrüchiger auf einer Insel sammelt man unter anderem Gegenstände ein und erntet Pflanzen, jagt Tiere und arbeitet mit Werkzeugen. Zum Beispiel kann man mit einem Messer und einer Kokosnuss man Trinkwasser erzeugen (wobei die Kokosnuss verbraucht wird), und mit einem Schwein und einem Messer erzeugt man Haut und Fleisch, und aus der Haut kann man Leder machen, und aus dem Leder Riemen, und so weiter.

Ha, dachte ich mir, das ist nett, das sieht objektorientiert aus, das könnte ich doch auch mal mit Schülern machen. Erster Gedanke:

Klassendiagramm

Das Messer kriegt folgende Methoden:

  1. eine Methode anwendenAuf(Kokosnuss)
  2. eine Methode anwendenAuf(Schwein)
  3. eine Methode anwendenAuf(Spielobjekt)
    (eventuell verzichtbar, da ererbt, aber siehe weiter unten)

Diese drei Methoden heißen zwar alle gleich, sind aber trotzdem verschiedene Methoden und machen verschiedene Sachen – dieses Konzept heißt overloading. – Ein anderes Konzept ist das des overriding, wenn eine Unterklasse die gleichnamige Methode der Oberklasse überschreibt. Das ist der Fall, wenn Methode (3) die ererbte gleichnamige Methode überschreibt. Siehe weiter unten. – Wenn man die Methode anwendenAuf mit einem Kokosnuss-Objekt als Parameter aufruft, wird Methode (1) ausgewählt und ausgeführt, wenn man sie mit einem Schwein-Objekt aufruft, wird (2) ausgeführt, wenn man sie mit irgendeinem anderen Spielobjekt-Objekt als Parameter aufruft, wird die Methode (3) ausgeführt.

Dann würde jede Schülerin, jeder Schüler eine eigene Klasse erzeugen (Messer, Kokosnuss, Hammer, Feuerstein, Zange.…) und würde für jede interessante Interaktion mit einem anderen Objekttyp eine eigene anwendenAuf-Methode schreiben – und die Catch-all-Methode für die restlichen Objekttypen übernehmen. Das fände ich ordentlich.

Das funktioniert auch wie gedacht, aber leider nur erst einmal, und dann doch nicht: In der Regel wird man eine Liste oder ein Array von Spielobjekten haben, und der Spieler wählt eins davon aus und wendet sein Messer darauf an. Und dann wird automatisch und stets die Methode (3) aufgerufen, auch wenn sich hinter dem Spielobjekt eine Kokosnuss verbirgt. Will heißen, wenn ich folgenden Code ausführe:

Spielobjekt o = new Kokosnuss();
Kokosnuss k = new Kokosnuss();
Spielobjekt m = new Messer();
m.anwendenAuf(o);
m.anwendenAuf(k);

Dann wird bei m.anwendenAuf(o) die Methode (3) aufgerufen, und nur bei m.anwendenAuf(k) wird die Methode (1). Denn wenn die ausführbaren Java-Klassen erzeugt werden, also beim Compilieren, also Übersetzen des Quellcodes, dann gilt: Die Kokosnuss o ist vom Typ Spielobjekt, basta. Das heißt static binding. Bei diesem Überstzen wird dann festgelegt, dass bei m.anwendenAuf(o) eben die Methode (3) ausgeführt wird. Und das geschieht dann auch, wenn das Programm tatsächlich ausgeführt wird.

Zwar hat beim Ausführen des Programms das Objekt o dann den Typ Kokosnuss, wie man herausfinden kann durch einen Aufruf von System.out.println( o.getClass() ). Aber das bringt nichts mehr, da ist die Wahl bereits getroffen. Etwas anders ist es bei ererbten und überschriebenen Methoden. Hier noch einmal die Zeilen von oben:

Spielobjekt o = new Kokosnuss();
Spielobjekt m = new Messer();
m.anwendenAuf(o);

In der letzten Zeile wird immer noch die Methode (3) anwendenAuf(Spielobjekt) aufgerufen. Aber es ist die Methode, die in der Klasse Messer definiert ist, und nicht die gleichnamige in der übergeordneten Klasse Spielobjekt. Das ist das overriding von vorhin, und dabei wird erst zur Laufzeit entschieden, also wenn das Programm tatsächlich läuft, welche der beiden Methoden aufgerufen wird. Die Maschine schaut zur Laufzeit, von welchem Typ m wirklich ist (nämlich Messer, und nicht Spielobjekt), und wählt dann die entsprechende Methode. Das heißt dann dynamic binding.

Kurz gesagt:

  1. Wenn ein Objekt eine ererbte Methode mit einer eigenen überschreibt, wird stets letztere ausgeführt. In der Unterklasse entscheidet man also selber, ob die ererbte Methode benutzt werden soll oder ob es stattdessen eine eigene Methode geben wird.
  2. Wenn ein Objekt allerdings als Parameter einer Methode übergeben wird, hat die Klasse, die diese Methode zur Verfügung stellt, keine Auswahlmöglichkeit und kann den Objekttyp des Parameters nicht als Auswahlkriterium heranziehen. Der wird vorher festgelegt auf das, was da steht, egal welche Klasse wirklich dahinter steckt.

Fazit: Die Klasse Messer braucht nur eine einzige Methode anwendenAuf(Spielobjekt). Man muss das ganze so machen:

void anwendenAuf(Spielobjekt o) {
  if (o instanceof Kokosnuss) {
    // ...
  }
  else if (o instanceof Schwein) {
    // ...
  }
  else {
    // ...
  }
}

Ich finde das ein bisschen weniger übersichtlich, als für jedes Objekt eine eigene Methode zu haben. Außerdem stört mich der instanceof-Operator. Mit dem kann man herausfinden, ob ein Objekt zu einer bestimmten Klasse (oder: Oberklasse davon) gehört. Er hat aber einen schlechten Ruf, weil er oft ein Anzeichen dafür ist, dass man nicht wirklich objektorientiert programmiert, weil sich seine Verwendung oft ersetzen lässt durch ordentliche Vererbung und überschriebene Methoden. Hier gibt es aber keine andere Lösung. Zugegeben: Man kann für Spielobjekt-Klassen ein eigenes Typ-Attribut einführen und es sich mit getTyp() geben lassen, und das als Entscheidungskriterium hernehmen. Dann hat jedes Kokosnussobjekt ein Attribut String typ = "kokosnuss", was mir aber auch nicht gefällt. Aber immerhin käme ich so um das Erklären von instanceof herum.

So oder so habe ich ein Problem. Nehmen wir an, ich habe Patronen, mit einem Attribut int anzahl, und Pistolen mit einem Attribut boolean geladen. Es könnte auch ein Zauberstab mit Ladungen sein, oder Goldmünzen:

class Patrone extends Spielobjekt {
 
  void anwendenAuf(Spielobjekt o) {
    if (o instanceof Pistole) {
      if (((Pistole) o).geladen == true) {
        // nichts, Pistole ist bereits geladen
      }
      else {
        ((Pistole) o).geladen = true; // Pistole ist jetzt geladen
        anzahl = anzahl-1;  // der Patronenhaufen wird um 1 reduziert
        if (anzahl == 0 ) { 
          entfernenAusInventar(this); // Patronenhaufen wird evtl. geloescht
        }
      }
    }
  }
 
}

Wenn ich in anwendenAuf auf das übergebene Objekt zugreifen möchte, muss ich einen Cast machen, also das Objekt manuell einer Unterklasse zuweisen. Das kann zu einem Fehler während des Ausführens führen, wenn das Objekt gar nicht zu dieser manuell zugewiesenen Klasse gehört. Wenn es gar nicht zu dieser manuell zugewiesenen Klasse gehören kann, wird bereits beim Compilieren/Übersetzen ein Fehler gemeldet. Dieses Casten ist für Schüler auch nicht leicht nachzuvollziehen, und auch eher schlechter Stil, wenn es denn anders geht. Aber: Es geht wohl nicht.

Später sieht das dann so aus:

Benutzeroberfläche Inselspiel
Wenn man zuerst auf das Schwert und dann auf eine Kuh klickt, verschwindet die Kuh aus dem Inventar und drei Rindfleisch tauchen auf.

Hmmm… wenn man eine Methode anwendenAuf (Spielobjekt []) einführt, kann man auch das Durchlaufen von Arrays üben. Mal sehen, wie ich das einbauen kann.

Fußnote:

Eine verlockende Sackgasse ist folgender Gedanke, mit dem der ursprüngliche Plan, verschiedene anwendenAuf-Methoden zu haben, doch noch verwirklicht werden könnte, wenn es den ginge.

Class c = o.getClass(); // wird zur Laufzeit bestimmt
c oNeu = (c) o; // müsste beim Compilieren überprüft werden können
anwendenAuf(c)

Man lässt sich von dem Spielobjekt o die Klasse geben, und castet dann o auf seine tatsächliche Klasse. Aber das geht nun einmal nicht, compiliert nicht einmal. Kurz: Ich kann nicht auf eine Klassentyp casten, der in einer Variablen gespeichert ist, weil dann bei der Compilierung nicht überprüft werden kann, ob das überhaupt ein grundsätzlich möglicher Cast wäre. Denn in einer Variablen kann ja alles stecken. Das weiß man erst zur Laufzeit.

Weitere Designfragen:

(1) Diese Regeln, was geschehen soll, wenn man das Messer auf die Kokosnuss anwendet – wo sollen die hin? Man kann es machen wie oben beschrieben, dann ist jede Klasse zuständig für sich selber. Wenn eine neue Klasse (etwa: Hühnchen) eingeführt werden soll, muss man in allen Klassen, deren Objekte etwas mit den Objekten der neuen Klasse anfangen können sollen, den Code ändern. Das ist umständlich. Stattdessen könnte man eine einzige Klasse haben, die alle möglichen Kombinationen von anwendenAuf(Spielobjekt o1, Spielobjekt o2) behandelt. Großer Vorteil: Führe ich neue Klassen ein, muss ich nur in einer Klasse Code ändern. Nachteil: Diese Methode wird ziemlich groß, da die Kombinationsmöglichkeiten mit der Anzahl der Klassen schnell steigen, auch wenn, zugegeben, nur die potentiell möglichen Kombinationen implmenetiert werden müssen. Bei 5 Typen gibt es maximal 25 Kombinationen, bei 10 Typen schon 100, das wächst geradezu… nein, nicht exponentiell, noch lange nicht, aber polynomiell/quadratisch. Und vor allem kann ich es dann nicht mehr so machen, dass jede Schülerin und jeder Schüler ihre eigenen Anwendungsregeln in eigenen Klassen erstellen.

(2) Richtig viel Arbeit kann ich mir machen, wenn ich Verb-Objekte einführe, also etwa eine Klasse Schneiden, die enthält, was geschieht, wenn ich ein Objekt des Interface-Typs “KannSchneiden” auf ein Objekt des Interface-Typs “KannGeschnittenWerden” anwende. So ähnlich ist das zwar bei Sprachen für Textadventures, aber so gründlich will ich gar nicht sein.

(3) Wenn ich vorher weiß, welche Unterklassen von Spielobjekt es geben soll, dann kann ich in die Spielobjekt-Oberklasse 30 leere, gegebenenfalls zu überschreibende Methoden platziere: anwendenAuf(Kokosnuss k) {}, anwendenAuf(Palme p) {} und so weiter. Dazu kommt dann eine zentrale Methode:

void anwendenAuf(Spielobjekt o) { 
  if (o instanceof Messer) anwendenAuf( (Kokosnuss) o); 
  else if (o instanceof Messer) anwendenAuf( (Palme) o); 
}

Diese Methode verteilt die Aufrufe dann auf die glechnamigen Methoden mit den korrekten Argumenten. Dann greift wieder das overriding und damit dynamic binding. Dann kann ich wieder meine ursprüngliche Idee umsetzen – separate Methoden für jeden Spielobjekttyp. Allerdings muss ich halt jedesmal den Code in dieser einen Klasse anpassen, wenn eine neue Unterklasse von Spielobjekt eingeführt wird.

(4) Keine Lösung habe ich bisher für das Folgende: Wenn ich ein Messer auf eine Kokosnuss anwende, geschieht dann das gleiche, wie wenn ich eine Kokosnuss auf ein Messer anwende? Wenn ich die Patronen anwende auf die Pistole, soll das gleiche passieren wie bei der Anwendung der Pistole auf die Patronen? Wenn nein, kein Problem, dann passt alles. Wenn ja… die Messerklasse entscheidet nur, wenn das Messer angewendet wird. In welcher Klasse soll stehen, was geschieht, wenn ich die Kokosnuss anwende? Wenn das in Kokosnuss ist, führt das zu Code-Dopplung, und wo sonst? Hier fällt mir nur ein, doch Lösung (1) zu wählen.

Links, die zeigen, dass viele Leute schon vor dem gleichen Problem standen:

(Und die Linksammlung erklärt, warum es nervig ist, wenn der Webfilter der Schule stackoverflow.com sperrt.)

Der Java-Klassendiagramm-Implementierungs-Aufgaben-Generator

Benutzeroberfläche Programm

Nachtrag: aktualisierte Fassung des Programms ist am Ende des Beitrags

Das mit dem Verstehen ist ja schön und gut, aber bei Sprachen – Programmiersprachen wie anderen – reicht es nicht, wenn man die grammatischen Kategorien kennt, man muss die Sprache auch anwenden können. In Bayern lernen die Schüler in Informatik in der 10. Klasse Gymnasium (im naturwissenschaftlich-technologischen Zweig) die Grundprinzipien es objektorientierten Modellierens und Programmierens. Mit den Prinzipien haben sie auch wenig Schwierigkeiten; Klassendiagramme kriegen sie einigermaßen hin – aber das Umsetzen der Diagramme in Programmcode macht vielen mehr Probleme.

Also habe ich mir vorgenommen, im nächsten Durchgang mehr zu pauken. Eine Doppelstunde nur Klassendiagramme implementieren. Und dann nochmal, wenn es nötig ist. Das kriegt man nämlich nur durch Übung rein. – Ob ich das dann wirklich so mache, hängt natürlich von der konkreten Klasse ab. Aber ich habe mir schon etwas vorbereitet, und zwar den Java-Klassendiagramm-Implementierungs-Aufgaben-Generator. So sieht er aus:

Benutzeroberfläche Programm

Wenn man auf den weißen Knopf kriegt, erzeugt und speichert das Programm zwei Textdateien. Die eine ist ein Klassendiagramm für eine zufällig erstellte Klasse, mit 1 zufällig ausgewählten Attribut, einem Standardkonstruktor und je einer getter- und setter-Methode für das Attribut:

|--------------------
| Raumschiff
|--------------------
| alter: int
|--------------------
| Raumschiff()
| getAlter(): int
| setAlter(int): void
|--------------------

Wenn man sparsam mit den Leerzeilen ist, kann man das in sechs kurzen Zeilen programmieren. Wenn die Schülerin das getan hat, importiert sie die zweite Datei, die das Programm erzeugt hat, in ihr Projekt. Diese Datei enthält den Code für eine weitere Java-Klasse, und zwar eine Testklasse, die überprüft, ob die Klasse auch semantisch richtig implementiert worden ist, also ob die Methoden auch das tun, was sie sollen, und ob das Attribut da ist, und ob die Datentypen und die Schreibung exakt mit dem Klassendiagramm übereinstimmen. Die Testklasse sieht so aus, auch wenn die Schülerin sich den Code nie anschauen muss:

import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
 
public class RaumschiffTest {
 
  Raumschiff testObject;
 
  public RaumschiffTest() {
  }
 
  @Before
  public void setUp() {
    testObject = new Raumschiff();
  }
 
  @After
  public void tearDown() {
    testObject=null;
  }
 
  @Test
  public void attributeUeberpruefen() {
    testObject.alter = 8;
  }
 
  @Test
  public void setAlterTest() {
    testObject.setAlter(40);
    assertEquals(40, testObject.alter);
    testObject.setAlter(11);
    assertEquals(11, testObject.alter);
    testObject.setAlter(31);
    assertEquals(31, testObject.alter);
  }
 
  @Test
  public void getAlterTest() {
    testObject.alter = -5;
    assertEquals(-5, testObject.getAlter());
    testObject.alter = 64;
    assertEquals(64, testObject.getAlter());
    testObject.alter = -17;
    assertEquals(-17, testObject.getAlter());
  }
 
}

Mit einem Mausklick startet man den Test, und herauskommt eine Anzeige, welche Tests bestanden wurde:

Java-Testergebnis

Dazu muss die von der Schülerin erstellte Klasse erst existieren, und zwar grammatisch fehlerfrei. Die Tests überprüfen nur, ob das Programm auch das tut, was es soll, nicht ob es überhaupt läuft – das sagt einem schon der Compiler eine Stufe zuvor.

Man kann sich auch Klassen mit zwei, drei oder vier Attributen erzeugen lassen. Wenn die Attribute nicht als private markiert sind, werden die Attribute überprüft und die getter/setter-Methoden anhand dieser Attribute überprüft – ansonsten wird die getter-Methode anhand der setter-Methode auf Richtigkeit getestet, und umgekehrt. Man auch auswählen, dass ein zweiter Konstruktor mit zu den Attributen passenden Argumenten erzeugt werden soll. Außerdem kann man noch ankreuzen, ob die neuen Objekte der Klasse bestimmte Startwerte für die Attribute haben sollen, die dann im Konstruktor festgelegt werden. Dann heißt es unter dem Klassendiagramm:

Der Wert des Attributs 'alter' soll am Anfang sein: 32

Die Werte für die Attribute werden ebenfalls zufällig ausgewählt. Attribute können vorerst nur vom Typ String, boolean, char, double, int sein. Die Testklasse wird jeweils unter Berücksichtigung all dieser Entscheidungen erstellt.

Das könnte man jetzt ausweiten und die Schüler selber Klassenbezeichner und Attribute auswählen lassen. Oder mit Referenzattributen arbeiten, dass also – für den Anfang – zwei Klassendiagramme erstellt werden, wobei das eine ein Referenz auf das zweite enthält.

Wen es stört, dass das so eine Art Liegestützen für Java sind, also eher sinnlose, wiederholende Übungen, der kann sich vielleicht daran erfreuen, dass die Schüler so an den Umgang mit Testklassen und Testverfahren herangeführt werden.

  • Download des .jar-Archivs. Vorsicht: Erzeugt im selben Verzeichnis die Textdateien im Format <Klasse>Test.java und <Klasse>Diagramm.txt und überschreibt vorhandene Dateien mit gleichem Namen ohne Rückfrage.
    • Erste Bugs gefunden, verbesserte Version von 2017-08-06, 19:50 Uhr
    • Neue Version vom 2017-08-07, 09:00 Uhr, etwas aufgeräumt im Code und assertEquals bei double verbessert
    • Neue Version vom 2017-08-31: Code aufgeräumt, die Varianten bei privaten Attributen etwas eingeschränkt, und jetzt kann man auch noch optional eine zufällige Methode schreiben lassen, die kein Getter oder Setter ist. Im Moment sind das aber nur istGerade, verdoppeln, hoch3 und betrag, das wäre aber leicht zu erweitern. Hintergrund: Jetzt gibt es doch für jeden Pseudo-Datentyp eine eigene Klasse (und damit Postleitzahlen im Bereich 10000–99999 statt mit beliebigen int-Werten), und für jede Pseudo-Methode ebenfalls (die für ihren eigenen Testcode verantwortlich ist).
    • Neue Version vom 2017-09-02. Nach außen unverändert, aber viel innen umbenannt und neu gebastelt, unter anderem zusätzliche Pseudo-Methoden.
    • Neue Version vom 2018-10-21. Postleitzahlen funktionieren jetzt als String.

ZEIT-Bibliothek der 100 Bücher

Titelbild Suhrkamp-Taschenbuch

Neulich ging es um Literaturkanons, und da wurde ich auf diese Sammlung hingewiesen: 1978 bis 1980 erschienen die Kolumnen in der Zeit, es wurde jeweils ein Werk der Weltliteratur in einem kurzen beschreibenden Essay vorgestellt.

Titelbild Suhrkamp-Taschenbuch

Die Sammlung ist selbst ein Zeitdokument. Man merkt, dass sie noch aus einer prä-postmodernen Zeit stammt: “Krimi wird nun nicht geboten, es sein denn, man hielte Dostojewskis ‘Dämonen’ für einen”, schreibt Herausgeber Fritz J. Raddatz gönnerhaft in seinem Vorwort. Die Grenze zwischen U- und E‑Literatur wird noch ganz hart gezogen. Der Beitrag zu Dantes Göttlicher Komödie beginnt: “Die Deutschen haben ein gestörtes Verhältnis zu Dante.” Man möchte den Autor schütteln, oder in den Arm nehmen, denn nein, den Deutschen ist Dante so etwas von egal, die haben gar kein Verhältnis zu ihm. Dass unter den 100 Werken gerade mal ein einziges ist, das von einer Frau geschrieben wurde, ist den Herausgebern selber aufgefallen; im Vorwort wird diese Beobachtung – wiederum gönnerhaft – gelobt als “Argument, das die sympatische kritische Wachheit unserer weiblichen leser dokumentiert.” Aber das ist kein Problem, denn ausgewählt wurde “ausschließlich nach literarischen Kriterien.” Diese Kriterien zu hinterfragen, so weit war man damals noch nicht. In der Jury saßen nur Männer, und – adding insult to injury – der Beitrag zum Buch der einzigen Autorin, Anna Seghers’ Das siebte Kreuz, ist der einzige Beitrag, der nicht von namhaften Intellektuellen verfasst wurde, sondern von einer 17-jährigen Schülerin.

Von den hundert Werken habe ich etwa ein Viertel gelesen. Weltliteratur ist drin, mit Schwerpunkt Deutschland, und daneben Europa, das ist legitim. Ganz wenig Amerika, gar kein Asien oder Afrika. Swift, Fielding und Sterne, aber weder Jane Austen noch eine Brontë-Schwester noch Mary Shelley. Das Nibelungenlied ist drin, in Ordnung – ist das eigentlich Weltliteratur, oder hätte man das in Deutschland nur gerne? Trotzkis Autobiographie ist drin, ein wenig sonderbar. Goethes Werther wird als avantgardistisch gelobt, weil man so unmittelbar dran ist an dem armen Mann. Dafür

opfert man dann gern den schon damals allwissend und schwatzhaft sich selber feiernden, sogenannten “ironischen” Erzähler, der uns mit seinen entsetzlich langen Romanen, in denen er auf Zehenspitzen hin- und herhuschend alles hübsch und artig dekoriert, bis zum heutigen Tage zu Tode langweilt. (Reinhard Lettau)

Also, ich mag den lieber als den Werther. – Die Auswahl hat mich bisher dazu gebracht, Faulkner zu lesen (nicht mein Fall), mich wieder mal an die Langwerke Kafkas zu machen (interessieren mich viel weniger als die Kurzprosa), Kleist wiederzulesen (immer ein Vergnügen) und Hebel (dito). Spuren von Ernst Bloch klang interessant und ist in der Post.