Forum: PC-Programmierung Objektorientiertes Problem mit kleinem Projekt


von wanderameise123 (Gast)


Lesenswert?

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!

von Oliver K. (okraits)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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.

von wanderameise123 (Gast)


Lesenswert?

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?

von Oliver K. (okraits)


Lesenswert?

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.

von wanderameise123 (Gast)


Lesenswert?

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.

von Oliver K. (okraits)


Lesenswert?

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.

von wanderameise123 (Gast)


Lesenswert?

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       = gcnew Game_Engine();
2
this->playground     = gcnew Playground(game);
3
this->user_interface = gcnew User_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?

von Oliver K. (okraits)


Lesenswert?

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?

von wanderameise123 (Gast)


Lesenswert?

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.

von Oliver K. (okraits)


Lesenswert?

Nun, so eine Referenz ist ja gleich übergeben ;) Du kannst es ja erstmal 
so lassen und es bei Bedarf dann ändern.

von Sven P. (Gast)


Lesenswert?

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.

von wanderameise123 (Gast)


Lesenswert?

das ist mir etwas zu hoch....

von Claudio H. (bastelfinger)


Lesenswert?

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.

von wanderameise123 (Gast)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Claudio H. (bastelfinger)


Lesenswert?

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!

von Sven P. (Gast)


Lesenswert?

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...).

von U.R. Schmitt (Gast)


Lesenswert?

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ß :-)

von Sven P. (Gast)


Lesenswert?

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 :-)

von ... ... ... (Gast)


Lesenswert?

Sven P. schrieb:
> Eine Datei löschen? Da sind wir wieder bei ANSI C :-)
1
std::remove(foobar);

von wanderameise123 (Gast)


Lesenswert?

gut danke werde es mir mal anschauen.

von Sven P. (Gast)


Lesenswert?

... ... ... schrieb:
> Sven P. schrieb:
>> Eine Datei löschen? Da sind wir wieder bei ANSI C :-)
>
1
std::remove(foobar);

Ja, und std::remove() ist ganz klar reinrassiges C++. So mit errno und 
char-Zeiger :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.