Allgemeine Frage zu OOP,... Ordnung muß sein
-
Hallo
Ich programmiere ganz gerne mit dem SDL in C++ oder Python
Ich habe mir jeweils ganz nützliche 2D-Frameworks geschrieben, mit verschiedenen
Klassen für Init, Grafik , Sound , Image/ Sprite etc. ...wie 1000 andere vor mir
Jetzt habe ich angefangen mir eine Kartendeck-Klasse zu erstellen.
Meine Frage zum Objekt-Orientierten-Programmieren:Das Kartendeck enthält ja 32 oder 52 Instanzen einer Klasse/ Struktur: KARTE
Sollte man eine Instanz eines Sprites/ Images einer Karte in die Kartenklasse
packen? Oder sollte die Kartenklasse nur wesentliches wie Name, Farbe, Wertigkeit beinhalten?
D.h: Das Image-handling für jede einzelne Karte wird in einer anderen Klasse zB
Grafics oder so behandelt? Imo eher leichter portabel, da man mit dem Memberimage sonst immer auf den SDL abhängig ist...Welcher Weg ist der sinnvollere?
-
die saubere methode liegt darin den kleinsten gemeinsamen brauchbaren faktor rauszufiltern den du benötigst, und den in einen tighten abstraktionslayer zu legen.
-
Dieses Problem hat mich auch schon mehrmals beschäftigt.
In der Vergangenheit habe ich es oft so gelöst, dass jeder Spielobjekt-Repräsentant (in deinem Fall eine Karte) ein eigenes Sprite besitzt. Ich dachte, ich wäre damit flexibler und nahm auch naiverweise an, es würde sich besser auf die Performance auswirken. Diese dürfte jedoch recht irrelevant sein, da ein Schreibvorgang/temporäres Objekt im Vergleich zu Grafikrendering kaum Rechenzeit beansprucht. Wenn es wirklich kritisch wird, kann man immer noch profilen und experimentieren.
Jetzt würde ich Grafik und Spieltechnik/Logik wohl eher trennen. Ein Ansatz wäre, eine Grafikverwaltungsklasse zu kreieren, der man die Aufgabe übertragen könnte, temporäre Sprites zu erstellen, um Spielelemente zu zeichnen. Die Spielelemente selber wären dann auf ein Minimum beschränkt, was sich auch gut auf den Speicherverbrauch auswirken würde. Ausserdem wären spiellogische Operationen wie Kartenmischeln etc. viel weniger schwerfällig, da wirklich nur die relevanten Daten umkopiert werden müssten.
-
Nexus schrieb:
Ausserdem wären spiellogische Operationen wie Kartenmischeln etc. viel weniger schwerfällig, da wirklich nur die relevanten Daten umkopiert werden müssten.
Das alleine klingt schon sehr sinnvoll !
Btw Mischen: Deswegen gefällt mir python

meine_liste = (karte_1,....,karte_32) random.shuffle( meine_liste )OK, die funktion in C ist auch schnell geschrieben, aber kann sie auch gemischte Datentypen mischen (oder enthalten) ?

-
Deswegen gefällt mir C++:

#include <algorithm> #include <Karte.hpp> int main() { std::vector<Karte> Deck; // hier Container befüllen std::random_shuffle(Deck.begin(), Deck.end()); // Deck ist nun gemischelt }Für ausführlichere Informationen zur STL siehe www.cplusplus.com und die Artikel dieses Forums.
-
sdl-fan schrieb:
Btw Mischen: Deswegen gefällt mir python

meine_liste = [b](karte_1,....,karte_32)[/b] random.shuffle( meine_liste )Nexus schrieb:
Deswegen gefällt mir C++:

std::vector<Karte> Deck; // hier Container befüllen :arrow_right: Geht in C++ nicht so einfach wie in Python std::random_shuffle(Deck.begin(), Deck.end());
-
einen schrieb:
// hier Container befüllen :arrow_right: Geht in C++ nicht so einfach wie in PythonIch meinte auch eher das
random_shuffle(). Das Feature mit der Initialisierungsliste für Container wird übrigens im nächsten C++-Standard enthalten sein.Momentan muss man noch
push_back()benutzen, was aber nicht schlimm ist. Gerade für dieses Beispiel eignet sich eine Schleife sowieso viel mehr als eine riesenlange Initialisierungsliste.Die Links zu den STL-Artikeln hier im Forum, deren Begutachtung sich auf alle Fälle lohnt:
1) Container
2) Iteratoren und Algorithmen
3) Hilfsklassen und Erweiterungen
-
Sry, ich will den Thread nicht überreizen, aber was ich mit dem python-Vorteil meinte war:
Erstelle oder Mische bitte in C/ C++ Listen, Vektoren, etc die verschiedene! datentypen gleichzeitig beinhalten

-
sdl-fan-002 schrieb:
Erstelle oder Mische bitte in C/ C++ Listen, Vektoren, etc die verschiedene! datentypen gleichzeitig beinhalten

Wozu braucht man das? Man muss dann trotzdem jeden Typ einzeln handhaben und Fälle unterscheiden (und: es ginge in C++ auch, z.B. mit Boost.Any).

-
Nexus schrieb:
Wozu braucht man das?
Naja, man erhält eine art dynamische struktur...
Btw mag ich C++ lieber als Python, aber Python ist so irre einfach und man kann kleine Ideen in 1/3 der Zeit umsetzen...
-
sdl-fan-003 schrieb:
Btw mag ich C++ lieber als Python, aber Python ist so irre einfach und man kann kleine Ideen in 1/3 der Zeit umsetzen...
Ja, C++ ist halt bei vielen Dingen relativ mühsam und man hat teilweise verhältnismässig lange für kleine Dinge. Das ist aber wieder eine Frage der Übung...
Aber zu deiner anderen Antwort: Das würde mich wirklich interessieren, nicht weil ich einen C++-vs.-Python-Flame anstrebe, sondern weil es mich tatsächlich wundert. Was meinst du mit "dynamischer Struktur"? Wenn einfach alle Typen vorkommen können, wie will man diese spezifisch ansprechen? Man muss ja irgendwo noch Typinformationen abspeichern, und da finde ich es eleganter, die Fälle gleich separat handzuhaben.
-
Nexus schrieb:
Man muss ja irgendwo noch Typinformationen abspeichern, und da finde ich es eleganter, die Fälle gleich separat handzuhaben.
Das ist das wunderbare an Python: Jede Variable kann einen beliebigen Datentypen beinhalten, ohne dafür spezifisch deklariert worden zu sein.
Imo reicht es die Länge der Liste zu ermitteln und dann bis zum Ende durchzuiterieren...Btw Thx Nexus für (nicht nur hier) kreative und informative Antworten, dickes Lob.
-
sdl-fan-002 schrieb:
Erstelle oder Mische bitte in C/ C++ Listen, Vektoren, etc die verschiedene! datentypen gleichzeitig beinhalten

das ist in c++ prinzipell auch möglich. nicht c++ ist das problem, sondern dessen 'dumb user base', die z.b. fragen stellt wie:
Nexus schrieb:
Wozu braucht man das?
-
c++fan.2008 schrieb:
nicht c++ ist das problem, sondern dessen 'dumb user base', die z.b. fragen stellt wie:
Nexus schrieb:
Wozu braucht man das?
Genau solch kritische Fragen, können manchmal das Bewußtsein anregen...
-
sdl-fan-004 schrieb:
Das ist das wunderbare an Python: Jede Variable kann einen beliebigen Datentypen beinhalten, ohne dafür spezifisch deklariert worden zu sein.
Imo reicht es die Länge der Liste zu ermitteln und dann bis zum Ende durchzuiterieren...Aber beim Durchiterieren ist es ja jedes Mal anders, was man mit dem aktuellen Element macht, oder? Wäre es von daher nicht einfacher, von Anfang an jeden Typ einzeln zu behandeln?
Tut mir leid, ich kann es mir nicht ganz vorstellen, vielleicht bin ich als C++-Programmierer auch diesbezüglich etwas zu engstirnig.

sdl-fan-004 schrieb:
Genau solch kritische Fragen, können manchmal das Bewußtsein anregen...
Wie wahr. Aber abgesehen davon hat es sowieso keinen Wert, unregistrierten Benutzern, die nichts zum Thema beitragen und diskutierfreudige Leute als "dumb users" bezeichnen, auch nur irgendeine Beachtung zu schenken.
sdl-fan-004 schrieb:
Btw Thx Nexus für (nicht nur hier) kreative und informative Antworten, dickes Lob.
Ich helfe gerne, und finde es nett, wenn Leute das auch schätzen.

-
Aber beim Durchiterieren ist es ja jedes Mal anders, was man mit dem aktuellen Element macht, oder? Wäre es von daher nicht einfacher, von Anfang an jeden Typ einzeln zu behandeln?
Nicht unbedingt. Wenn sie die gleiche Schnittstelle haben (z.B Ausgabe), kannst du das ganz gut benutzen. C++ hat da ja ein ähnliches Konzept, nennt sich abstrakte Basisklasse.
( Jetzt sollten dir auch ein paar Anwendungsbeispiele einfallen. :))
-
Die Geschichte mit den verschiedenen Datentypen in einem Container ist eben einer der grundlegenden Unterschiede zwischen C++ und Python: statische vs. dynamische Typisierung. beides hat je nach Anwendungsgebiet seine Vor- und Nachteile und seine Daseinsberechtigung.
Bei dynamischer Typisierung kann man wirklich alles in einen Container stopfen. Dafür muss man später bei der Verarbeitung damit rechnen, dass einzelne Elemente eine benötigte Operation nicht anbieten.
Bei statischer Typisierung muss alles vom gleichen Typ sein (bzw. mit polymorphen Zeigern von verwandten Typen), dafür kann man davon ausgehen dass alle Elemente die gleichen Operationen anbieten.Zusammengefasst heißt das in dem Beispiel also: Flexibilität vs. Sicherheit
-
sdl-fan-004 schrieb:
[Das ist das wunderbare an Python: Jede Variable kann einen beliebigen Datentypen beinhalten, ohne dafür spezifisch deklariert worden zu sein.
Imo reicht es die Länge der Liste zu ermitteln und dann bis zum Ende durchzuiterieren...Das nennt man "Weak Typing". Bei C++ wurde bewusst auf "Strong Typing" gesetzt, weil es allgemein die Betriebssicherheit erhöht. Implzite Konvertierungen können z.B. ziemlich böse sein, wenn man nicht aufpasst und sich z.B. verschreibt.
So ziemlich auf die Spitze wird das Strong Typing in Ada getrieben, wo man u.U. Integers nicht einander zuweisen kann.
Beispiel:type Decibel_Type is new Float; --Typ fuer Pegel in dB type Frequency_Type is new Float; --Typ fuer Frequenzen in Hz Freq : Frequency_Type := 0.0; --Deklaration/Initialisierung Level : Decibel_Type := 10.0; --Deklaration/Initialisierung Freq := Level; --Versuch einer Zuweisung, aber Frequenzen != Pegel -> Error Freq := Frequency_Type(Level); -- Typecast. Das geht, aber man muss dem Compiler extra sagen, dass man solchen Unsinn machen willMan kann also z.B., wenn man es auf die Spitze treibt, für verschiedene Maßeinheiten unterschiedliche Typen definieren. Im obigen Beispiel sind beide Typen zwar Float, aber eben trotzdem nicht gleich. Warum das, trotz des höheren Schreibaufwands ein Vorteil sein kann, sollte wohl klar sein, vor allem, wenn es sich um ein Interface handelt, dass von vielen Programmierern benutzt wird.
Das Typsystem von C++ ist nicht ganz so streng, aber auf jeden Fall schon mal deutlich sicherer als irgendwelche Sprachen mit völlig loser Typisierung.
-
du sagst, das Typsystem in C++ sei nicht ganz so streng, das kann man so oder so sehen. Es ist nur nicht so einfach, einen neuen Typen zu erschaffen, der den gleichen Wertebereich hat wie ein anderer Typ. Dein Beispiel sähe in C++ etwa so aus:
struct Decibel_Type { float value; explicit Decibel_Type(float f) : value(f) {} operator float() {return value; } }; struct Frequency_Type { //analog }; Frequency_Type Freq = 0.0f; Decibel_Type Level = 10.0f; Freq = Level; //Versuch einer Zuweisung, aber Frequenzen != Pegel -> Error Freq = Frequency_Type(static_cast<float>(Level)); // Typecast. Das geht, aber man muss dem Compiler extra sagen, dass man solchen Unsinn machen will(den ganzen privtae/public-krams und andere Dinge mal außer acht gelassen)
-
pumuckl schrieb:
[...]
Jo, aber Dein Beispiel lässt problemlos sowas zu:
float unsinn = Freq + Level; //oder sowas Frequency_Type Freq(0.0f); Decibel_Type Level(Freq);In Ada würde auch das nicht gehen. Mal ganz zu schweigen vom Aufwand den man für Zuweisungen treiben muss. Das würden dann zig Float-Wrapper werden, und was man von POD-Wrappern zu halten hat, sollte wohl klar sein.
Beispiel:Decibel_Type level(0.0f); Decibel_Type inc(3.0f); level = level + inc; //geht nichtPS: Das erinnert mich etwas an den Nachbau von OO in C. Das sind Features, die hat die Sprache einfach nicht. Man kann es nachbauen, aber das ist sehr fehleranfällig und mühsam. Kosten/Nutzen stehen nicht im gesunden Verhältnis zueinander.