Hallo,
bin gerade dabei in C++/CLI ein kleines Spiel zu programmieren.
Die vorkommenden Klassen habe ich schon im voraus erarbeitet. Da wäre
die GAME_ENGINE die die Logik implementiert und Berechnungen, etc...
durchführt, das USER_INTERFACE, welches die Benutzereingaben
entgegennimmt und schließlich der PLAYGROUND, der die Ausgabe übernimmt.
http://i.imgur.com/rDE7d.png
Da der Ausgangspunkt von allem die Form1 ist, stellt sich mir die Frage
wie die Struktur auszusehen hat, also die Verbindung der Klassen
untereinander.
Wer erzeugt wen? Wie kommunizieren die Klassen untereinander. Meine
erste Idee war es, die GAME_ENGINE zu erzeugen und eine Referenz von den
anderen Beiden Klassen zu übergeben (http://i.imgur.com/v12uD.png).
Wie würdet ihr es machen?
Danke im voraus!
Die Realisierung der Logik könnte ich mir mit Hilfe eines
Zustandsautomaten vorstellen.
Dieser könnte als Klasse realisiert werden, welche Methoden zur
Steuerung des Ablaufs nach außen anbietet. Diese Methoden werden durch
Events, die das Benutzerinterface auslöst, aufgerufen. Somit verfügt das
Benutzerinterface über eine Referenz auf den Zustandsautomaten.
Der Playground als Klasse bietet nach außen ebenfalls Methoden an, durch
die versch. Ausgaben angestoßen werden. Dies macht der Automat, somit
benötigt er eine Referenz auf den Playground.
Das Benutzerinterface ist ja schon in der Form gekapselt.
Man könnte entweder in der Form Instanzen von Automat und Playground
erzeugen oder noch eine extra Klasse machen, die Instanzen von Form,
Automat und Playground erzeugt.
wanderameise123 schrieb:> Die vorkommenden Klassen habe ich schon im voraus erarbeitet. Da wäre> die GAME_ENGINE die die Logik implementiert und Berechnungen, etc...> durchführt, das USER_INTERFACE, welches die Benutzereingaben> entgegennimmt und schließlich der PLAYGROUND, der die Ausgabe übernimmt.
Aus meiner bescheidenen Erfahrung mit der Thematik:
Die Strukturierung klingt furchtbar nach dem, was man in der Schule von
planlosen Lehrkräften im Informatikunterricht eingeprügelt bekommt.
Playground und User_Interface kannst du schlecht trennen, beides gehört
zur Benutzerschittstelle. Auch ist es vermutlich Unsinn, die
Benutzereingaben überhaupt noch durch eine Klasse zu würgen -- du
arbeitest doch sicherlich Ereignisorientiert. Nutze die Ereignisse, die
deine Umgebung dir bereitstellt, evtl. inklusive Validatoren und
solcherlei.
Was macht Playground dann anders, als deine Formularklasse, die du
ohnehin schon instantiierst?
Für die Logik -- ich kenne dein Spiel ja nicht... -- bieten sich
meistens Zustandsautomaten an.
Richtig interessant für OOP, so als nicht ganz realitätsnahes, aber
lehrsames Beispiel, wäre es so: Jedes Lebewesen im Spiel ist ein Objekt.
Und diese Objekte können selbstständig feststellen, wer um sie herum
lebt...
Du würdest dir dann auch automatisch Gedanken darüber machen, wer wen
erzeugt und wer wen und auf welche Art wieder zerstört.
Implementiert werden soll Conways, somit wäre jede Zelle ein Objekt, und
hätte eigenschaften wie tot oder lebendig und anzahl lebender nachbarn.
Ok könnte man machen. Dann würde jede Zelle selbst berechnen, wie ihr
nächster Zustand wäre oder?
Somit bleibt die Verbindung zum user interface. Dann wäre jede Zelle
auch ein grafisches Element?zB ein rectangle.
Trotzdem muss eine übergeordnete instanz die elemente verwalten und zB
auf die user eingaben reagieren?
Entweder Du läßt jede Zelle selbst ihren Zustand in der Folgegeneration
berechnen oder Du iterierst von außen über alle Zellen und übergibst den
Zellen dann den Folgezustand.
Jo, Du kannst in der Klasse für eine Zelle ihre Koordinaten hinterlegen
und ihr Zustand (tot oder lebendig) wird dann beim Aktualisieren der
Darstellung in gefüllt/nicht gefüllt umgesetzt.
Nun, wie mein Vorredner schon sagte, ist das Userinterface
ereignisorientiert, d.h. es wird ein Click-, Scroll- oder Changed-Event
ausgelöst und Du mußt dann im entsprechenden Eventhandler die
entsprechende Aktion von Automat oder Playground anstoßen, also z.b.
Neuzeichnen der Grafik oder Berechnen der Folgegeneration oder was auch
immer. Eine extra Klasse brauchst Du dafür nicht, es gibt ja bereits die
Form.
stimmt, aber wenn ich die form missbrauche, dann geht die modularität
flöten oder? angenommen das user interface soll austauschbar sein, wäre
es falsch alles in der form unterzubringen!? Zudem kann ich die arbeit
so aufteilen und jeder entwickelt unabhängig, wenn man es in eine extra
klasse verlagern würde.
die frage ist dann nur wie die Austauschbarkeit der klassen
untereinander passiert.
Deshalb hab ich ja gemeint, wenn die Instanzen von Automat und
Playground an einer übergeordneten Stelle und nicht in der Form erzeugt
werden, dann kannst Du das Userinterface auch austauschen, weil die
Logik, die Eingabe und die Ausgabe jeweils gekapselt sind.
In der Form hast Du ja normalerweise nur die Eventhandler drin, und die
rufen Methoden von Automat und Playground auf.
Du könntest dann den Playground zunächst als Interface definieren, das
man dann auf verschiedene Art und Weise implementieren kann, z.B. als
GUI-Version oder als Kommandozeilenversion.
also danke erstmal, denke das klingt alles soweit ok und logisch. Aber
im moment möchte ich gerne einen anderen ansatz verfolgen und würde
gerne wissen was du davon hälst:
1
this->game=gcnewGame_Engine();
2
this->playground=gcnewPlayground(game);
3
this->user_interface=gcnewUser_Interface(game);
Ich habe die 3 klassen in der form1 instantiiert und eine Referenz der
game engine an die beiden anderen klassen übergeben. So dient die game
engine als austauschmodul. wäre das auch eine möglichkeit?
Grundsätzlich wäre das natürlich eine Möglichkeit, aber wie möchtest Du
von der Gameengine aus den Playground steuern (für die Grafikausgabe
z.B.), wenn Du in der Gameengine keine Referenz auf den Playground hast?
Oder soll das von der Form aus gemacht werden?
ja das ist mir auch aufgefallen, es fehlt die referenz in die andere
richtung. keine ahnung, was wäre denn sinnvoll? es würde es gerne so
umsetzen, da ich es schon soweit habe.
Vielleicht solltest du dich trotzdem von deiner Komposition (Referenzen
rumreichen) lösen.
Was hälst du denn davon, eine abstrakte Klasse 'Game' zu implementieren,
die virtuelle Methoden besitzt, um mit dem Benutzer zu reden?
Ein fertiges Spiel würde diese abstrakte Klasse dann ableiten und die
virtuellen Methode vervollständigen.
Du machst dir Gedanken über Objektorientierung, das ist GUT. Dir würde
jetzt folgendes helfen:
http://de.wikipedia.org/wiki/GRASP
Besser ist es aber hier erklärt, das Buch ist anhand seines Titelbilds
schon ein Muss:
http://www.amazon.de/Applying-UML-Patterns-Introduction-Object-Oriented/dp/0131489062/
Mit den GRASP-Patterns bekommst du Regeln an die Hand, mit denen du eine
Lösung bzgl. lose Kopplung, hoher Zusammenhalt, Erweiterbarkeit etc.
bewerten kannst. Und die Patterns sagen dir, welches Objekt etwas tuen
soll.
genau das ist nämlcih das Problem. Ich erstelle Klassen, die bestimmte
Dinge erledigen. Ich stelle getter/setter methoden bereit um alles
nötige zu kapseln. Letztendlich fehlt mir aber das Wissen, wie ich alles
in Verbindung bringe und vernetze. Ich merke wie ich trotz Klassen,
alles wieder zB sequentiell mache. Irgendwie unterschätzt man das Thema!
wanderameise123 schrieb:> genau das ist nämlcih das Problem. Ich erstelle Klassen, die bestimmte> Dinge erledigen. Ich stelle getter/setter methoden bereit um alles> nötige zu kapseln. Letztendlich fehlt mir aber das Wissen, wie ich alles> in Verbindung bringe und vernetze.
Falscher Ansatz
Man erstellt nicht einfach Klassen, damit man Klassen hat.
Man überlegt sich im Vorfeld welche Klassen man braucht, wofür eine
Klasse zuständig ist, welche Aufgabe sie hat und wie alle Klassen
miteinander zusammenspielen.
Und erst dann gehts mit der Implementierung los.
Getter und Setter sind nicht das, warum man Klassen baut. Die sind ein
notwendiges Übel. Die Power einer Klasse besteht in den restlichen
Memberfunktionen, welche Arbeit die verrichten können. Wenn du nur
Getter und Setter hast, hättest du auch Strukturen benutzen können, das
wäre kaum ein Unterschied.
Oft ist es auch einfacher, zuerst die Objekte zu analysieren, und dann
daraus die Klassen abzuleiten. Ein schönes Objektdiagramm zeichnen, das
hilft dir dann a) die Klassen zu identifizieren, b) Regeln und
Constraints (z.B. Multiplizitäten) zu bestimmen und c) Testdaten zu
haben.
Übrigens besitzt eine Instanz einer Klasse nicht nur Attribute und
Operationen, sondern auch eine inhärente Identität. Diese Identität
siehst du genau in einem Objektdiagramm.
Kauf dir wirklich das oben empfohlene Buch, da ist alles super erklärt!
Schreib dein Programm doch einfach mal konventionell, also strukturiert.
Wenn du dabei das Gefühl hast, dass du was richtig Sauberes erarbeitet
hast, weißt du, warum man OOP nicht braucht. Ich kenne viele Leute, die
konventionell (also ganz ohne OOP) viel saubereren und leichter zu
wartenden Quelltext produzieren, als andre mit OOP, Templates und was
der Compiler noch so hergibt. Ist doch völlig ok.
Wenn du dabei das Gefühl hast, dass alles verkorkst daherkommt, siehst
du vielleicht eher, wo OOP dir das Leben erleichtert. Ich kenne auch
Leute, die mit OOP die dollsten Programme konstruieren, aber völlig
aufgeschmissen sind, wenns um eine blöde verkettete Liste geht, die ohne
OOP geschrieben wurde (GTK...).
Karl heinz Buchegger schrieb:> Getter und Setter sind nicht das, warum man Klassen baut. Die sind ein> notwendiges Übel. Die Power einer Klasse besteht in den restlichen> Memberfunktionen, welche Arbeit die verrichten können. Wenn du nur> Getter und Setter hast, hättest du auch Strukturen benutzen können, das> wäre kaum ein Unterschied.
Pfui, da mischt der alte C Hacker wieder Ansi C mit rein :-)
(das ist ein Grund warum ich persönlich die Hybridsprache C++ nicht
ausstehen kann, wenn man Code in die Hand kriegt ist das meist ein
wüstes Gewächs aus klassischen C und Objektorientiertheit. Ich will hier
keine Diskussion pro und Contra auslösen, nur mein persönliches Problem
mit C++)
Wenn objektorientiert dann bitte richtig, Strukturen und allein
rumstehende Daten gibts nicht mehr, wohl aber Klassen die ggf. nur Daten
kapseln. (z.B. ein 2 oder 3 dimensionaler Punkt)
Insofern sind Getter und Setter kein notwendiges Übel.
Ansonsten hast Du natürlich recht, die eigentliche Arbeit wird nicht von
Getter und Setter verrichtet.
Gruß :-)
U.R. Schmitt schrieb:> Pfui, da mischt der alte C Hacker wieder Ansi C mit rein :-)> (das ist ein Grund warum ich persönlich die Hybridsprache C++ nicht> ausstehen kann, wenn man Code in die Hand kriegt ist das meist ein> wüstes Gewächs aus klassischen C und Objektorientiertheit.
Das ist so abwegig auch nicht.
Mit BOOST hat sich das zwar geändert. Aber mit C++ alleine stehst du bei
den meisten alltäglichen Aufgaben immer noch ziemlich dumm herum.
Eine Datei löschen? Da sind wir wieder bei ANSI C :-)