www.r-krell.de |
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr |
Willkommen/Übersicht > Informatik > SWE-2
mit Vorgehensmodellen, UML-Klassendiagramm, Struktogramm
und dem Kartenspielprojekt "Rot & Schwarz"
mit spielbarem Applet und vollständigem Java-Quelltext
Auf dieser Seite finden Sie:
Zurück zur Übersicht über alle meine Seiten zur 'Informatik mit Java' auf meiner "Informatik"-Hauptseite
zum Seitenanfang / zum Seitenende
Meine erste Seite zum Softwareengineering habe ich bereits vor einigen Jahren konzipiert. Inzwischen haben sich die für das Zentralabitur geforderten Inhalte verändert (u.a. werden die eEPKs und UML-Sequenzdiagramme nicht mehr verlangt). Und ich habe inzwischen verschiedene Zugänge zum Thema Softwareengineering ausprobiert. Insofern soll diese Seite kein Ersatz für meine ältere, erste Seite SWE zum Softwareengineering sein, sondern ist eher als Alternative oder Ergänzung mit zusätzlichen Schwerpunkten (Projektarbeit und Phasenmodelle), aber auch mit gemeinsamen Inhalten (wie etwa dem UML-Klassendiagramm) zu verstehen.
Hinweis: Alle im Folgenden verwendeten oder gezeigten Software-Programme sind kostenlos (Zeichenprogramme, Javaeditor..). Downloadmöglichkeiten bzw. Links zu den Herstellerseiten sowie Verweise auf weitere fremde Seiten finden sich ganz am Ende dieser Seite im Kapitel "Verweise & Software-Quellen").
zum Seitenanfang / zum Seitenende
Schülerinnen und Schüler sehen erfahrungsgemäß erst dann die Notwendigkeit, über sinnvolles Vorgehen bei Software-Projekten nachzudenken, wenn sie schon einige Zeit auf Projektarbeit verwendet haben (und dabei zwangsläufig selbst Probleme hatten). Erst in der Nachbereitung oder besser noch vor einem neuen Programmierprojekt wächst die Bereitschaft, etwas über geschicktes Vorgehen bei Projekten zu lernen.
Auch wird erst nach eigener Erfahrung klar, dass mehrere Teammitglieder mit unterschiedlichen Aufgaben bei einem gemeinsamen Projekt ihre Teilaufgaben nicht beginnen sollten, bevor nicht ein gemeinsamer Grobentwurf mit verbindlicher Festlegung der Schnittstellen ordentlich durchdacht und mehrfach diskutiert und besprochen feststeht. Insbesondere bedeutet das, dass zunächst z.B. ein UML-Diagramm aller geplanten Klassen entwickelt werden muss, so dass Klassen-, Attribut- und Methodennamen sowie die erlaubten Zugriffe mit den zu übergebenden Parametern und den zurück zu gebenden Ergebnissen klar benannt und aufgeschrieben sind!
Erst dann kann jede Teilgruppe bzw. jedes Teammitglied eine oder mehrere Klassen für den Feinentwurf und die Programmierung erhalten, wenn es genau weiß, was es liefern muss und was es von anderen verwenden bzw. erwarten kann. Beim ersten Projekt will man normalerweise viel zu früh endlich loslegen. Doch gilt auch hier die alte Weisheit: "Solange die Richtung unklar ist, ist Geschwindigkeit kein Vorteil".
zum Seitenanfang / zum Seitenende
Zur Beschreibung des Vorgehens bei einem (Software-)Projekt diente zunächst vor allem das Wasserfall-Modell:
Die einzelnen Phasen (hier fünf; z.T. werden auch sechs Phasen angegeben, wobei dann meist Grob- und Fein-Entwurf getrennt sind) folgen streng sequentiell hintereinander (wie Wasser bei einem terrassierten Wasserfall immer nur vom oberen ins nächstuntere Becken fließen kann). Im klassischen Modell waren keine Ausnahmen vorgesehen. Aber natürlich sollen z.B. beim Testen gefundene Fehler behoben werden können, wozu der Programmtext geändert werden muss. Deshalb gilt inzwischen das Zurückgehen um höchstens eine Stufe als zulässig. Es ist aber nicht erlaubt, bei einem im Test festgestellten Fehler eine Methode ganz neu zu konzipieren (Entwurf), sondern es dürfen wirklich nur reine Programmierfehler verbessert werden. Und wenn beim Programmieren ein Problem auftaucht oder etwas unklar erscheint, darf nicht zur Analyse zurück gegangen werden. Auch wenn dieses Verbot dazu zwingen soll, jede Phase besonders sorgfältig zu erarbeiten und abzuschließen, zeigt die Praxis, dass trotz aller Vorsicht gelegentlich Korrekturen an früheren Entscheidungen vorgenommen werden müssen.
Das Gegenmodell ist das Spiralmodell, das immer wieder ein Eintreten in die verschiedenen Phasen erlaubt. Gegenüber üblichen Darstellungen habe ich hier die Quadranten ähnlich wie beim Wasserfallmodell bezeichnet:
So könnte vielleicht mit der Oberfläche und einer Kernfunktion eines Programms angefangen und diese programmiert werden, damit man sich nach ersten Tests und grundsätzlicher Zufriedenheit weiteren Teilen der Aufgabe zuwendet, um dann diese erst genauer zu untersuchen, zu beschreiben, eine Umsetzung zu entwerfen und sie zu realisieren bzw. dem Projekt hinzuzufügen. Oder man bemerkt nach den Tests oder im (Probe-)Betrieb Unzulänglichkeiten und steigt erneut in die Analyse- und Entwurfsphase ein, um das gleiche Problem in einem neuen Anlauf geschickter anzugehen. Dabei bleibt offen, ob hier nur die Arbeit des Softwareentwicklers beschrieben wird, oder aber ob der Kunde bzw. Abnehmer/Anwender in den Testprozess einbezogen wird und sich weitere Funktionen oder Verbesserungen der Software wünschen darf (oft nur gegen zusätzliche Bezahlung). Die verschiedenen Prototyping-Modelle - Wikipedia nennt z.B. 6 Arten von Prototyping, wobei insbesondere das experimentelle, das explorative und das evolutionäre Prototyping hervor gehoben werden sollen - sind mehr oder weniger Ausformungen des Spiralmodells.
Tatsächlich existieren heute viele Erweiterungen oder Mischformen der beiden Grund-Modelle. Offenbar wird für umfangreiche Projekte oft auch ein bestimmtes Vorgehensmodell zwischen Kunde und Entwickler verbindlich vereinbart. Bei Abschluss bestimmter Phasen muss der Kunde dann auch die bisherige Leistung bezahlen -- ähnlich wie beim schlüsselfertigen Hausbau nach Ausschachtung, Fertigstellung der Kellerdecke, des Rohbaus usw. Abschlagszahlungen des Bauherrn an den Generalunternehmer in festgelegter Höhe auf den vereinbarten Gesamtpreis fällig werden. Dass hierbei natürlich ein stringentes Modell wie das Wasserfallmodell oder das daraus abgeleitete V-Modell ein Beziffern der Teilleistungen leichter macht, als das Spiralmodell (von dem man vorher nicht weiß, wie viele 'Windungen' nötig werden), liegt auf der Hand.
Darüber hinaus kommt es natürlich auch auf den Einsatzzweck der Software an, welches Modell geeigneter ist: Eine Steuersoftware für U-Bahnen wird man lieber nach ausführlichen Tests nach dem Wasserfallmodell in Betrieb nehmen wollen, als sich auf den Probebetrieb einer nach dem Spiralmodell bzw. dem experimentellen Prototyping vorläufig erstellten Software einzulassen, die zwar schon den Halt an Bahnhöfen, aber noch nicht die Berücksichtigung von Ampeln oder das Vermeiden von Auffahrunfällen enthält. Solange allerdings noch Fahrerinnen oder Fahrer die letztgenannten Aufgaben wahrnehmen, wäre auch eine schrittweise Einführung der Automatisierung möglich. Speziell wird Bürosoftware oder werden Spiele oft erst nach der Rückmeldung von alpha- und beta-Testern weiter ausgeformt und entwickelt, bevor sie auf den Markt kommt/kommen. Und letztlich folgt auch die langfristige Entwicklung von Betriebssystemen (etwa Windows 8.1 nach früheren Versionen, begonnen etwa mit Windows 3.1 / 98 / ME / XP / Vista usw.), Textverarbeitern (z.B. Word 2013 nach Word 2010, Word 2007 oder Word 2003/2004 bzw. ebenso die verschiedenen Versionen anderer Hersteller oder OpenSource-Projekte) oder Java-Releases (inzwischen Java 1.7 nach etwa 1.5, 1.4.2, 1.3.1, 1.2 oder 1.1.8) insgesamt einem Spiralzyklus.
Auch bei den dagegen eher kleinen Softwareprojekten im Informatik-Unterricht dürfte das Spiralmodell das Vorgehen besser beschreiben: Zumindest rät man als erfahrener Lehrer immer wieder (leider aber manchmal vergeblich), dass Schülerinnen und Schüler zunächst eine sehr einfache, lauffähige Grundversion nur mit den allerwichtigsten Eigenschaften fertigstellen sollen, die dann in kleinen Schritten verbessert und erweitert wird, bevor sie sich im Programmieren aufwändiger Grafik, umfangreichen Kontrollen von Benutzereingaben und schönen, aber nicht unbedingt nötigen Features verlieren und damit oft gar nicht zu einem lauffähigen Gesamt-Programm kommen. Wie im Abschnitt 'Motivation' angedeutet, wird dieser Rat nach einschlägigen Erfahrungen schülerseits ernster genommen und überhaupt eher zugestanden, dass zunächst eingehende Analyse- und Entwurfsphasen sinnvoll sind, die langfristig sogar Zeit und Arbeit sparen.
zum Seitenanfang / zum Seitenende
Im Folgenden soll die Entwicklung eines Java-Programms für ein einfaches Kartenspiel dargestellt werden. Das spezielle Spiel wurde
ausgewählt, weil es leicht zu verstehen und nicht allzu aufwändig ist, die Implementation den im Unterricht gerade zuvor besprochenen
abstrakten Datentyp 'Schlange' nutzen kann und es schließlich auch mit einer netten Grafik ausgestattet werden kann, die für
Schülerinnen und Schüler den Reiz eines Projekts (gegenüber den als trockener empfundenen normalen Unterrichtsprogrammen)
ausmacht.
[Nur zur Ergänzung sei angemerkt, dass sich weitere Anwendungen der dort auch vorgestellten linearen abstrakten Datentypen Keller und Schlange
etwa bei den Wegsuchen im Labyrinth-Programm (auch als interaktives Applet) auf meiner Seite "Informatik mit
Java, Teil e)" finden lassen.]
Es handelt sich um ein Glücksspiel für 2 Personen. Gespielt wird mit allen 32 (oder 52) Karten eines
Kartenspiels (ein Kartendeck oder Blatt; aber ohne Joker). Zu Anfang erhält jeder der beiden Spieler die Hälfte aller Karten, nämlich ein Spieler alle roten Karten (alle Herz- und Karo-Karten), der andere alle schwarzen Karten (Kreuz und Pik). Jeder Spieler mischt seine Karten gut durch und legt sie als Packen mit der Bildseite nach unten vor sich. Der Spieler mit den roten Karten deckt seine oberste Karte auf und legt sie offen auf den Tisch. Der zweite Spieler legt nun ebenfalls seine oberste Karte darauf. Wer von beiden Spielern die Karte mit dem höheren Wert gelegt hat, gewinnt die offen liegenden Karten (den "Stich"). Die gewonnenen Karten werden wieder umgedreht und kommen unter den Packen des Gewinners (wodurch sich im Laufe des Spiels die Farben durchmischen). Der Gewinner des letzten Stichs "spielt aus", legt also wieder die oberste Karte seines Packens offen auf den Tisch und der andere Spieler legt seine Karte dazu. Immer erhält der Spieler mit der höheren Karte den Stich und spielt anschließend seine oberste Karte auf. Sind allerdings die aufgedeckten Karten beider Spieler gleichwertig, bleiben sie offen auf dem Tisch liegen und jeder Spieler legt eine weitere Karte offen darauf. Dies geschieht, bis einer der Spieler eine höhere Karte hat und damit den Stich aus allen bisher offen liegenden Karten gewinnt, d.h. zusammen schiebt, umdreht und unter seinen Packen legt. Das Spiel endet, wenn ein Spieler keine Karten mehr hat und dadurch verliert. Die Kartenwerte sind, aufsteigend angeordnet (von niedrig bis hoch): [2 - 3 - 4 - 5 - 6 -] 7 - 8 - 9 - 10 - Bube - Dame - König - Ass. Die Farbe ist für den Wert egal, beispielsweise sind Karo-Dame und Kreuz-Dame gleichwertig. Es gibt kein "Trumpf". |
Dieses Spiel, in einem Spielebuch als "Rot und Schwarz" oder "Tod und Leben" gefunden, kenne ich aus meiner Kindheit als "Idiotenspiel" oder "Doofmann", da es kein Können erfordert und immer nur von oben runter gespielt wird - und daher auch nach körperlicher Anstrengung oder mit halbem Sonnenstich im Freibad gut als Zeitvertreib dienen konnte. Von russischstämmigen Schülern wurde mir erzählt, dass sie das Spiel als "Betrunkener" kennen.
Es gibt zwei Varianten, die hier im Folgenden nicht berücksichtigt und nicht programmiert werden:
Mit der letzten Variante käme beim "Aussuchen" etwas Können, Geschick oder Strategie ins Spiel. Das wird hier aber nicht programmiert.
zum Seitenanfang / zum Seitenende
Ist das Spiel verstanden (und sicherheitshalber auch mal mit echten Karten von Hand gespielt worden) (Analyse), wird überlegt, wie das Spiel auf dem Bildschirm aussehen sollte. Je nach Fantasie und Erfahrung mit anderen Spielprogrammen sind verschiedene Ideen möglich bis hin zum Ziel, animierte Avatare in einer Art Videoszene spielen zu lassen. Hier soll allerdings eine deutlich weniger aufwändige Version verfolgt werden. Da es zwei Spieler gibt, sollen deren Namen erfragt und gespeichert werden. Dass sie beim Spielstart Karten erhalten, muss nicht unbedingt angezeigt werden (allerdings muss der Computer intern die Kartenpacken richtig verwalten). Dann müssen immer zwei Karten offen auf den Tisch gelegt werden - diese Karten sollen angezeigt werden (zunächst nur als Text, etwa "Herz 8" oder "Pik Bube" - Grafik kann bei einer späteren Version hinzugefügt werden). Schön wäre außerdem, wenn man sieht, wer den Stich erhält, wie viele Karten jeder Spieler hat und schließlich, wer verliert bzw. gewinnt. Die Oberfläche soll mit der Java-AWT bzw. mit Swing erzeugt werden. Das wäre die Aufgabenstellung.
zum Seitenanfang / zum Seitenende
Aus dem vorigen Abschnitt wird deutlich, dass das Programm wesentlich von seinem äußeren Erscheinungsbild abhängen wird. Es sollte daher zunächst der Entwurf einer einfachen (aber erweiterbaren) (Swing-)Oberfläche erfolgen, der auf Papier, mit einem Grafikprogramm oder durchaus direkt mit dem GUI-Builder z.B. des Java-Editors erzeugt werden kann:
Für Kontrollausgaben während der Programmentwicklung mag ein zusätzliches Textfenster unter der letzten Ein- bzw. eher Ausgabezeile (wo jetzt "Bitte Namen eingeben und [starten]" steht) sinnvoll sein. Die Kartenpacken selbst werden nicht dargestellt, ebensowenig ist eine Animation des Spielens oder Ziehens vorgesehen - es wechseln einfach die Inhalte der Textzeilen oder die angezeigten Zahlen. Wenn nach gleichwertigen Karten erneut Karten offen auf den Tisch gelegt werden, werden immer nur die letzten (obersten) Karten genannt bzw. angezeigt. Wie erwähnt, ist diese Darstellung nur eine von vielen Möglichkeiten.
zum Seitenanfang / zum Seitenende
Hat man sich grundsätzlich auf eine Oberfläche festgelegt, ist zu überlegen, was das dahinter liegende Programm können muss (Outside-In-Vorgehen): Von der Oberfläche ausgehend wird überlegt, was im Inneren des Programms vorgehen muss: Es muss die Funktionen des Spiels beherrschen und sollte für jede Schaltfläche eine Methode bereit stellen, die auf Knopfdruck evtl. Eingaben aus den Textfeldern übernimmt oder die nächste Spielsituation (nächstes Aufspielen / nächster Stich) herbeiführt und Daten (Bezeichnungen der nächsten Karten usw.) für die Anzeige in den dafür vorgesehenen Textfeldern liefert. Da Oberfläche und weitere, eigene Programmierung getrennt werden sollen, werden hierfür eine oder mehrere neue Klassen angelegt.
Gerade Anfänger tun sich mit der Klassenaufteilung oft schwer, weswegen der Klassenentwurf immer wieder (im Unterricht und zu Hause!) geübt werden muss. Als Faustregel für Unerfahrene mag gelten: Substantive in der Aufgabenstellung (hier etwa der Spielbeschreibung) bzw. wichtige Gegenstände werden durch ein eigenes Objekt und damit durch eine entsprechende Klasse realisiert; Verben im Aufgabentext beschreiben hingegen i.A. die Methoden. Ist also hier von zwei Spielern die Rede, die Karten aus einem Blatt (Deck) von 32 Skat- oder 52 Poker-Karten verwenden, diese mischen oder aufspielen, so liegt es nahe, einen Bauplan (=Klasse) für eine Karte (nach dem dann alle 32 oder 52 eigentlichen Karten als Objekte erzeugt werden können), eine Klasse für das Blatt, eine Klasse für einen Spieler (beide Spieler werden nach diesem einen gleichen Bauplan erzeugt), usw. vorzusehen. Aus "mischen" und "aufspielen" werden dann Methoden. Während jeder Spieler eine Karte aufspielen kann (und die Methode spieltAuf deswegen beim Spieler eingerichtet wird), können im Computer auch Gegenstände Aktionen ausführen. So geht es darum, das Kartenblatt zu mischen. Im echten Leben ist dazu ein Mensch oder eine Mischmaschine nötig, im objektorientierten Programm lassen wir dies das Blatt selbst machen und richten die Methode mischen als Fähigkeit vom Blatt ein. Allerdings muss man mit der "Substantiv=Klasse/Verb=Methode"-Faustregel etwas aufpassen: Substantive wie Wert oder Farbe beschreiben keine eigenen Gegenstände, sondern Eigenschaften einer Karte. Für solche Eigenschaften müssen selten eigene Klassen, sondern können Datenfelder (= Attribute) in der Karten-Klasse eingerichtet werden.
Andererseits sind für das Software-Projekt manchmal auch Klassen nötig, die in der Aufgabenbeschreibung nicht vor kommen. So muss irgendjemand oder irgendetwas als Ansprechpartner der Oberfläche (die übrigens auch eine Klasse zu ihrer Programmierung braucht) zur Verfügung stehen (Eingaben entgegennehmen und Ausgaben liefern) und dafür sorgen, dass die Spieler wirklich die Karten des Blatts nehmen und Karten aufspielen, Stiche machen, diese ordentlich einsammeln usw. - mit anderen Worten: spielen, bis ein Gewinner feststeht. Dazu wird die Klasse Spiel bzw. Spielkontrolle entworfen mit der zentralen Methode spielen (später aus technischen Gründen als run bezeichnet).
Hat man Erfahrung mit abstrakten Datentypen, so ist offensichtlich, dass die Kartenpacken vor jedem Spieler jeweils als eine (Warte-)Schlange von (Spiel-)Karten aufgefasst werden können (vgl. meine Seite "Informatik mit Java", Teil e)): von oben wird gezogen (vorne aus der Schlange geht's raus), unten (hinten) kommen die gewonnenen Karten des letzten Stichs dran. Auch zur Verwaltung der Karten auf dem Tisch ist eine dynamische Datenstruktur sinnvoll, da nicht immer nur zwei, sondern nach gleichwertigen Paaren auch mehrere Karten auf dem Tisch liegen können. Wird der ganze gewonnene Stich umgedreht, so gerät die zuerst aufgespielte (älteste, ursprünglich unterste Tisch-) Karte nach oben und kommt als erste hinten an den Packen des Gewinners. Auch für die Tischkarten eignet sich also ein FIFO-Speicher (first in, first out), mithin ebenfalls eine Schlange! Die insgesamt drei Schlangen (der Packen jedes Spielers und die Karten auf dem Tisch) können alle nach dem gleichen allgemeinen typisierbaren Bauplan einer Schlange erzeugt werden, der bereits aus früheren Anwendungen zur Verfügung steht. D.h., es muss eine Klasse Schlange (und dazu eine Klasse Knoten) geben. Elementtyp für Schlange und Knoten muss jeweils die Karte sein. Und wie gesagt: alle relevanten Angaben zu einer Karte (wie "Herz", "Bube", aber am besten auch ein leicht vergleichbarer numerischer Wert für die Rangfolge) werden Attribute. Ich habe z.B. jedem Buben die 12, der Dame die 13, dem König die 14 und dem As (neue Rechtschreibung Ass) den Zahlenwert 21 zugewiesen, während die einfachen Karten 2 (bzw. 7) bis 10 ihren (Typ-)Namen auch als (Zahlen-)Wert bekommen. Der höhere Wert sticht!
Da in den Schlangen nicht gemischt werden kann, müssen die Spieler anfangs jeweils schon gemischt entweder nur die schwarzen oder nur die roten Karten des Blatts erhalten. In der Klasse für das Blatt (=Kartendeck) wird einmal festgelegt, welche Karten es überhaupt gibt, und das Mischen als Methoden eingebaut. Die Spielkontrolle bzw. spielen muss später dafür sorgen, dass (sich) das Blatt erst mischt, bevor die Spieler ihre Karten kriegen bzw. nehmen.
Die Aufteilung in die verschiedenen Klassen wird als Grob- oder auch als Komponenten-Entwurf bezeichnet (weil in anderen Programmiersprachen die den Java-Klassen entsprechenden Programmstücke z.T. auch als Units oder Komponenten bezeichnet werden). Sinnvollerweise erfolgt der Grob-Entwurf grafisch mit einem UML-Klassen-Diagramm - auf Papier, mit einem Grafikprogramm wie etwa Dia oder aus dem "UML"-Menü des Javaeditors mit den Menüpunkten 'Neue Klasse' oder - wenn die bereits fertige Schlangen-Klasse (und die zugehörige Klasse Knoten) eingebaut werden soll(en) - mit 'Klasse öffnen'. Hier wurde Dia genutzt (das auch im Zentralabitur verwendet werden muss):
Nach der reinen Lehre müssen im UML-Diagramm in den Klassen keine Attribute angegeben werden, die nötig sind, um die gezeichneten Assoziationen, Aggregationen oder Kompositionen zu realisieren. Insofern gibt es z.B. in der Klasse Spieler kein Attribut für den Kartenpacken, weil die Aggregation mit einer Schlange schon ein solches nahelegt. Und über die Multiplizitäten (= Kardinalitäten) ist auch klar, dass in der Klasse Blatt nicht eine einfache Variable vom Typ Karte reicht, sondern eine Reihung (Array) oder Liste benötigt wird, die 32 bzw. 52 Karten aufnehmen kann. In zwei Fällen, wo solche unnötigen Attribute angegeben wurden, ist oben durch rote Kommentare darauf hingewiesen.
Vielfach wird auch verlangt, alle Attribute grundsätzlich als 'private' (-) zu erklären und immer passende öffentliche get- und set-Methoden zu definieren. Dies habe ich hier nicht gemacht, um die Darstellung nicht unnötig aufzublähen.
Weiterhin ist manchmal die Entscheidung Aggregation oder Komposition schwierig - ich bin mir da selbst auch oft unsicher (zum Glück sind beides 'hat'-Beziehungen, die im Javaprogramm letztlich gleich programmiert werden können). Da der rekursive Knoten ohne Knoten nicht existieren kann, wurde dort die Komposition gewählt. Hingegen können Knoten auch ohne Schlangen (z.B. in Kellern), eine einzelne Spielkarte auch ohne ganzes Blatt existieren, weswegen dafür immer nur eine Aggregation gezeichnet wurde. Auch Spieler könnten vielleicht ohne die Spielkontrolle (nämlich mit einem anderen Steuerprogramm) existieren - deswegen auch da nur die Aggregation. Und die Spielkontrolle könnte auch aus einer anderen Oberfläche aufgerufen werden, ist also als Teil vom aufrufenden Ganzen nicht existenziell abhängig (keine Komposition). Hingegen würden Textfelder und Schaltflächen ohne die Oberfläche keinen Sinn machen. Da sind aber keine Kompositionen gezeichnet, weil die Bildschirmelemente der Oberfläche gar nicht aufgeführt wurden.
Die Assoziationen ('kennt'-Beziehungen) sind hingegen einfacher zu erkennen. Dabei wäre vielleicht sogar ohne die eingezeichneten Linien
klar, dass der Knoten die als Inhalt enthaltene Karte kennen muss, oder dass der Spieler das Blatt kennen muss, von dem er anfangs die Karten
kriegt, usw. (Für Erklärungen zu den möglichen Beziehungen sei jedoch auf meine andere, erste Softwareengineering-Seite oder die
einschlägige Literatur zu UML-Klassendiagrammen verwiesen!)
Die Methoden sollten ihren Zweck durch sprechende Namen deutlich machen. Ob nun der Spieler anfangs alle Karten einer Farbe gemeinsam aus dem Blatt nimmt oder einzeln nach und nach oder ob er nach einem gewonnenen Stich die Karten wie hier einzeln vom Tisch nimmt oder in einem Schwung, ist letztlich egal. Der Tisch mit den offen liegenden Karten wurde hier übrigens nicht als eigene Klasse dargestellt, sondern als Attribut bzw. Teil der Spielkontrolle entworfen - so wie auch die Kartenpacken der Spieler nicht eine eigene Klasse erhalten haben, sondern als Attribute vom Typ Schlange (angedeutet durch die Aggregation vom Spieler zur Schlange) innerhalb der Klasse Spieler geplant sind. (Der bei Schlange und Knoten angegebene Elementtyp ist in allen Fällen immer nur Karte)
Besondere Aufmerksamkeit ist später noch auf die Methode spielen innerhalb der Spielkontrolle zu richten, die Zug um Zug bzw. Stich für Stich das Spiel ablaufen lassen soll, und zwischendurch immer wieder die gezogenen Karten und aktuellen Werte an die Oberfläche meldet. (Anfänglich werden die Namen der Spieler und die später von spielen zu aktualisierenden Bildschimkomponenten von der Oberfläche an den im Diagramm nicht eingetragenen Konstruktor der Klasse Spielkontrolle übergeben, damit spielen frei von Parametern bleiben kann).
zum Seitenanfang / zum Seitenende
Beim Feinentwurf geht es nun darum, das Innere der im Grobentwurf festgelegten Methoden zu planen, d.h. die Methodenrümpfe mit ihren
Algorithmen zu entwerfen. Die zu übergebenden Parameter, der Rückgabetyp und die Funktion dürfen nicht verändert werden, damit
sich andere parallel arbeitende Teammitglieder auf den Grobentwurf verlassen können.
Sofern Algorithmen mit Kontrollstrukturen benötigt werden, empfiehlt es sich, dafür Struktogramme oder Programmablaufpläne zu
zeichnen. Beispielhaft soll hier die Methode mischen aus der Klasse Blatt mit einem Struktogramm entworfen werden:
Vorausgesetzt wird hierbei, dass alle Karten des Blatts in einer Reihung (=Array) Karte[] reihe = new Karte[kartenAnzahl] stehen. Es wird darauf verzichtet, auf i != j zu prüfen, weil der (Zeit-)Aufwand für 100-faches Vergleichen vermutlich höher ist, als die vielleicht 3 bis 8 unnötigen Vertauschungen bei i == j.
Außerdem sollen noch die Algorithmen für zwei Methoden aus der Klasse Spieler angesprochen werden:
Am anspruchsvollsten ist natürlich die Methode spielen innerhalb der Spielkontrolle. Da sie jedoch von niemand sonst gebraucht wird, wurde auf ihren Feinentwurf zunächst leider wenig Mühe verwendet und gehofft, sie könne einfach so programmiert werden - was sich als Fehler herausstellte. Im nächsten Bildschirmausdruck weiter unten ist ein Teil des Programmtextes von spielen zu sehen, dort unter dem Methoden-Namen run .
zum Seitenanfang / zum Seitenende
Es soll hier nicht vorgeführt werden, wie die oben angesprochenen Algorithmen in Java-Quelltext umgesetzt werden. Nach und nach wurden die Methoden gefüllt, kompiliert, das Gesamtprojekt aus/durch die Oberfläche gestartet und kontrolliert, dass die neuen Methoden wunschgemäß funktionieren. Interessenten finden den fertigen Programmtext weiter unten komplett auf einer Extraseite.
Allerdings stellte sich heraus, dass das aufgeschobene, wenige Nachdenken über die zentrale spielen-Methode dieser nicht gut getan hatte: Nach Druck auf den [starten]-Knopf in der Oberfläche wurde zwar spiel.spielen aufgerufen (wobei spiel das in der Oberfläche definierte und erzeugte Objekt vom Typ Spielkontrolle ist) - aber der Knopf blieb gedrückt, bis das ganze Spiel fertig war (oder mit Gewalt abgebrochen wurde). Es gab keine Chance, während des Spiels die Schaltflächen [weiter] oder [automatisch] zu bedienen! Da spielen nämlich erst endet und die Kontrolle an die aufrufende Stelle in der Oberfläche zurück gibt, wenn das Spiel beendet ist, war die Oberfläche dadurch solange blockiert. Entweder musste spielen so zerlegt werden, dass jeder Aufruf das Spiel nur um einen Stich weiter bringt (dann wäre aber die Kontrolle des Gesamtspiels in die ActionPerformed-Methoden der Schaltflächen in der Oberfläche gewandert) oder Oberfläche und Spiel mussten quasi parallel ausgeführt werden, d.h. der [starten]-Knopf darf nur die Ausführung des Spiels in einem eigenen Thread anstoßen, muss aber sofort die Kontrolle zurück erhalten, auch wenn spielen noch nicht fertig ist. Threads erfordern, dass die angestoßene Methode run heißt, nichts zurück gibt ('void') und nichts erhält (keine Parameter). Letzteres gilt schon, sodass spielen nur in run umbenannt werden musste. Die Kommunikation zwischen Oberfläche und Spielkontrolle muss über eine zusätzliches Objekt erfolgen, auf dessen Datenfelder beide lesend und schreibend zugreifen können: Die Spielkontrolle meldet, wenn das Aufdecken eines Kartenpaars fertig ist; die Oberfläche meldet, wenn [weiter] oder [automatisch] gedrückt wird. Den Bauplan für dieses Austausch-Objekt ak bzw. aK habe ich Ablaufkontrolle genannt (und dafür den Namen der Spielkontrolle auf Spiel verkürzt). Zur allgemeinen Erläuterung nebenläufiger Prozesse in eigenen Threads verweise ich auf die entsprechenden Ausführungen auf meiner Informatik-Seite i) über Netzwerk-Programmierung.
Übrigens: Richtiges Testen ist eine Wissenschaft für sich, die zum Konstruieren eigener Testmengen u.ä. führt. Hier wurde hingegen das Spiel nur ein paar Mal gestartet und beobachtet und/oder es wurden verschiedene Eingaben (insbesondere andere Eingaben als "32" oder "52" für die Gesamtzahl der zu verwendenden Karten) ausprobiert. Aber vollständiges und systematisches Testen ist das nicht, reicht aber für ein Spiel aus (während man bei einer automatischen U-Bahn-Steuerung natürlich sicher sein will, dass wirklich alle möglichen [selbst die unwahrscheinlichen, an die nicht ohne Weiteres gedacht wird] Betriebs- und Störfälle berücksichtigt wurden und nicht zu Fehleren führen!).
Beim Testen machte ich eine interessante Beobachtung: Da die Karten vom aufspielenden Gewinner des letzten Stichs und dem nachlegenden zweiten Spieler ohne merkbare Verzögerung praktisch gleichzeitig aufgedeckt werden, hatte ich mir ursprünglich die Mühe geschenkt, wirklich den Gewinner des letzten Stichs zuerst aufspielen zu lassen, sondern immer Spieler1 (rot) und dann Spieler2 (schwarz) legen lassen. Den Stich gewinnt ja trotzdem der Richtige, sodass ich mir keine negativen Auswirkungen dieser Vereinfachung ausgemalt hatte. Bei Testspielen stellte sich aber heraus, dass die Spiele oft nach 1000 oder 2000 Stichen noch nicht beendet waren und als unentschieden abgebrochen werden mussten. So werden nämlich die roten und schwarzen Karten nicht wirklich durchmischt, sondern auf den geraden Positionen in jedem Packen und auf dem Tisch liegen immer die roten, in den ungeraden Postionen die schwarzen Karten. Das nun doch programmierte regelgerechte wechselnde Aufspiel durchmischt hingegen die Karten wirklich und führt praktisch nicht mehr zu Endlosspielen bzw. Unentschieden! Aber überzeugen Sie sich selbst und probieren das fertige Spiel (s. nächster Abschnitt), dem übrigens inzwischen noch die grafische Darstellung der aufgedeckten Karten hinzugefügt wurde. Dazu wurden die Klasse Karte um ein Attribut mit dem Namen der zur Karte gehörigen Bilddatei und die get-Methode nenneDateinamen ergänzt, und auf den vorher frei gelassenen Teilen der Oberfläche werden die Bilder in den JLabels jLbBildR und jLbBildS dargestellt (die dazu von der Oberfläche an die Spielkontrolle übergeben wurden) (bildordner enthält den Pfad zum Bilder-Verzeichnis). Außerdem wurde die Schlange um ein Attribut anzahl ergänzt, das bei den beiden Spielern r und s mit der Methode kartenZahl abgefragt wird, damit für jeden Spieler leicht die Dicke des Kartenstapels angezeigt werden kann:
Das aus dem fertigen Programm vom Java-Editor weitgehend automatisch erzeugte UML-Diagramm mit den endgültigen Namen sei hier nochmal angegeben. Es enthält allerdings immer alle Attribute (= Datenfelder = globale Variable) - auch solche, die die z.T. nachträglich von Hand zu ergänzenden oder abgeänderten Beziehungen verwirklichen (und deswegen eigentlich nicht angegeben werden müssten). Um das Diagramm nicht zu breit werden zu lassen, wurden Typangaben und Parameterlisten unterdrückt - der Java-Editor bietet hierzu verschiedene Darstellungs-Optionen. Und die mit kontrolle bezeichneten Methoden liefern i.A. eine Gesamtausgabe der jeweiligen Struktur, damit während der Programmentwicklung überprüft werden konnte, ob überall die erwarteten Inhalte erscheinen. Im fertigen Programm werden sie nicht benötigt.
Die Möglichkeit, von Hand Multiplizitäten nachzutragen, wurde nicht genutzt - dazu verweise ich auf das UML-Diagramm weiter oben.
Die main-Methode ist im Diagramm allerdings nicht zu sehen: sie befindet sich (um den Start sowohl als Application als auch als Applet zu
ermöglichen) in einer zusätzlichen Startdatei und erzeugt und startet die Oberfläche RS_SpielGUI (GUI = graphical user interface =
grafische Oberfläche). Der vollständige, kommentierte Quelltext findet sich auf einer Extraseite, die im nächsten Abschnitt
aufrufbar wird.
zum Seitenanfang / zum Seitenende
Das Bild zeigt verschieden Stadien aus der Anfangsphase des Spiels (wo rote und schwarze Karten noch nicht durchmischt sind):
Am besten begnügen Sie sich aber nicht mit einigen statischen Bildern, sondern probieren das Programm selber aus auf der
"Rot&Schwarz"-Extraseite mit Applet und Quelltext
zum Seitenanfang / zum Seitenende
Natürlich lässt sich mit einem Beispiel auf einer einzigen Seite (plus Extraseite) nicht jede Leserin und jeder Leser sofort zum
Systemanalytiker und genialen Programmierer machen. Zudem orientieren sich meine Darstellungen am Unterricht in der Q1, wo auf gut anderthalb Jahre
Informatik-Leistungskurs aufgebaut werden kann. Übung hilft wirklich und mit zunehmender Erfahrung fällt der Entwurf und
anschließend das gute Programmieren immer leichter (wobei der hier gezeigte Weg natürlich nur einer von vielen ist und es sicher auch
ganz andere gute Umsetzungen dieses Spiels geben kann). Ich hoffe, trotzdem einen verständlichen Einblick in die üblichen Techniken
gegeben zu haben und so zur Erfahrung beitragen zu können.
zum Seitenanfang / zum Seitenende
Bevor Sie einen der folgenden Verweise anklicken, sollten Sie erst meine Seite zu Ihren Favoriten hinzufügen oder ein Lesezeichen setzen, damit Sie anschließend sicher hierher zurück finden! Natürlich kann ich für den Inhalt fremder Seiten keine Verantwortung übernehmen. Wenn ein Verweis nicht funktioniert, dort inzwischen andere Inhalte zu sehen sind oder wenn Sie mir weitere passende Seiten empfehlen können, bitte ich um eine kurze Nachricht!
Hinweis: die fremden Seiten werden in einem neuen Browser-Fenster geöffnet. Geschieht beim Anklicken eines Verweises scheinbar nichts, wird das neue Fenster vermutlich vom aktuellen Fenster verdeckt -- bitte per Task-Leiste ins neue Fenster wechseln!
Informatik mit Java, Übersicht | Inhaltsverzeichnis (auf meiner "Informatik"-Hauptseite) mit Übersicht über alle meine Java-Seiten | |
Softwareengineering, Teil 1 | Meine andere (ältere) Seite zum Softwareengineering, in der vorgenannten Übersicht als Seite "SWE" aufgeführt. Sie enthält weiter Ausführungen zu den Techniken des objektorientierten Entwurfs am Beispiel einer modellierten Autovermietung. Außerdem gibt es auch weitere Verweise auf fremde Seiten zur systematischen Programmentwicklung. | |
Informatik mit Java, Teil e) | Hier habe ich abstrakte Datentypen behandelt, u.a. die oben verwendete Schlange (generisch im Anhang des dort angegebenen Referats). Im Juli 2013 dreht sich auch ein Arbeitsblatt und die 3. Klausur der Q1 auf meiner Seite "aktuelle Informatik-Klausuren aus der SII" u.a. um Schlangen. | |
Diagram Designer | Mit dem Diagram Designer von M. Vinther (Meesoft) wurden das Wasserfall- und das Spiralmodell gezeichnet. Verwendet wurde die Version 1.26 von 2013. | Alle diese Programme können gratis herunter geladen und privat und in der Schule kostenlos genutzt werden (Stand Juli/August 2013)! |
Java-Editor | Der Javaeditor wird an vielen Stellen gezeigt und ist die hier und im Unterricht verwendete Entwicklungsumgebung. Ich
empfehle ihn auch auf meiner "Informatik-mit-Java"-Seite a) als bestens geeignetes Werkzeug. Hier wurde die Version 11.39 vom Mai 2013 eingesetzt und gezeigt (ab Version 12 / Mitte August 2013 wäre sogar ein Struktogrammeditor integriert). |
|
Dia (portabel) Dia (installer) |
Das Zeichenprogramm Dia steht auch in einer portablen, hier verwendeten Variante zur Verfügung. Verwendet wurde die im Juli 2013 aktuelle Version 0.97.2. | |
Struktogramm Editor | Für die Struktogramme habe ich den Struktogramm-Editor 1.7 von Kevin Krummenauer (whiledo.de) verwendet (Nachtrag: Ab Version 12 / seit Mitte August 2013 können Struktogramme auch direkt im Java-Editor erzeugt werden!) | |
xskat.de Gratis-Spielkartenbilder |
Kostenlose Bilddateien von Spielkarten können z.B. bei den angegebenen Quellen herunter geladen werden. Früher wurden die in XSkat verwendeten Bilder auch getrennt zum Download angeboten. | |
Wikipedia Elektronikpraxis FH Regensburg |
Kleine Auswahl verschiedener Seiten zu Vorgehensmodellen (Phasenmodellen). Auf der Elektronikpraxis-Seite und in der pdf-Präsentation von Herrn Hafenrichter von der FH Regenburg wird auch das oben nur kurz erwähnte, in der Praxis aber verbreitete V-Modell dargestellt. Der Wikipedia-Artikel setzt sich durchaus kritisch mit dem Sinn solcher Modelle auseinander (Stand Juli 2013) | |
zurück zur Informatik-Hauptseite