Konstruktionsklassen auslagern?



  • Danke zunächst!

    Also ich drücke mich offensichtlich einfach nicht gut aus. Solche grundlegenden Sachen sind mir schon klar. Dass Hund und Hase vom Tier erben können ist nichts Neues; das hat allerdings auch nicht mit meinem spezifischen Problem zu tun. Ich habe mir die Fragen der Verantwortlichkeit und der logischen Unterteilbarkeit bereits gestellt. Ich frage hier, weil das einfach zu nichts geführt hat.

    Ich versuch es Mal genauer zu beschreiben, was ich unschön finde. Und bei jedem Punkt würde mich interessieren, a) ob es wirklich schlimm ist und b) wie ich es besser lösen könnte. Warum ich überhaupt frage? Weil ich intuitiv das Gefühl habe, es ist schlechter Stil, rational aber nicht darauf komme, wieso.

    Meine ModelLoader-Klasse ist im Prinzip nur eine große Funktion. Es gibt eine Instanz, man ruft eine Methode auf, diese klappert tausend Funktionen durch und liefert am Ende etwas zurück.

    => Idee wäre eine freie Funktion zu definieren, die viele andere freie Funktionen aufruft.
    => Problem: Es soll außer der Ladefunktion niemand die "Subfunktionen" aufrufen können.
    => Problem: Je nach Dateiformat möchte ich einen anderen Loader nutzen. Man soll später dynamisch weitere Loader hinzufügen können, die sich von der Funktionsweise (Format X -> MAGIC -> Engine-Format) nicht unterscheiden UND alle Zugriff auf die Interna von Model haben sollen. Das löse ich dadurch, dass die ModelLoader-Basisklasse mit Model befreundet ist und protected-Methoden bereitstellt, um Referenzen auf die Interna den einzelnen konkreten Loadern zu übergeben.

    Aus den Problemen ergibt sich für mich heraus, dass eine Klasse schon die korrekte Form zur Sammlung der Funktionen ist. Blöd ist, dass ich wie gesagt immer nur eine Instanz habe, was dagegen spricht. Wo ist die Lösung?

    Ich habe viele einzelne Methoden. Manche von diesen lassen sich vom Sinn zusammenfassen. Aber wie sollte ich diese wiederum als Elemente einer Klasse nochmal zusammenfassen?

    => Idee wäre eine weitere Klasse zu bauen, welche diese Methoden besitzt.
    => Problem: Die Klasse würde ich nur nutzen, weil mir nichts Besseres einfällt.
    => Problem: Entspricht das der Idee einer Klasse? Wieder Mal gäbe es nur eine Instanz, welche unterhalb des Loaders wäre, von dem es auch nur eine Instanz gibt.

    Ich würde also den Vorteil der Kapselung, die mir das Konzept Klasse bietet, ausnutzen, wenngleich nach meiner Vorstellung einer Klasse (Schablone für etwas mehrfach Instanziierbares) darunter leiden müsste. Intuitiv unsauber, rational keine Ahnung.

    Ich hoffe, mein Problem ist jetzt etwas deutlicher geworden.



  • Ja, so sieht es aus!
    Die Konvertierung ist eben nur ein Algorithmus, der ein Objekt bestimmten Typs zum Ergebnis haben sollte. Ich finde das ist ziemlich gut in einer freien Funktion aufgehoben. Die freien Helfer-Funktionen müssen gar nicht über die Modulgrenze hinaus sichtbar sein (anonymous namespace).
    Den Aufruf dieser Konvertierungsfunktion könntest du dann beispielsweise in Objekten, abgelitten von einer abstrakten Model-Converter-Klasse, erledigen, welche zudem noch die Dateinamenerweiterungen etc. überprüfen könnte und was weißt ich nicht und dann von einer Model-Loader-Registry dazu benutzt wird, um kontrete Modelle zu laden und in deinem Format auszuspucken.

    Und als ein Leitsatz unter der Beobachtung, dass das, was du da hast, bereits gut funktioniert: Was im Moment gut funktioniert ist praktisch immer besser als das was eigentlich besser wäre 😉



  • Eisflamme schrieb:

    Blöd ist, dass ich wie gesagt immer nur eine Instanz habe, was dagegen spricht. Wo ist die Lösung?

    Darin, dass du immer nur eine Instanz hast. Was ist daran blöd, was spricht dagegen? Das gibts auch als Entwurfsmuster: nennt sich Singleton

    Ich habe viele einzelne Methoden. Manche von diesen lassen sich vom Sinn zusammenfassen. Aber wie sollte ich diese wiederum als Elemente einer Klasse nochmal zusammenfassen?

    => Idee wäre eine weitere Klasse zu bauen, welche diese Methoden besitzt.
    => Problem: Die Klasse würde ich nur nutzen, weil mir nichts Besseres einfällt.
    => Problem: Entspricht das der Idee einer Klasse? Wieder Mal gäbe es nur eine Instanz, welche unterhalb des Loaders wäre, von dem es auch nur eine Instanz gibt.

    Schau mal nach dem "Single Responsibility Principle". Kapsele weg, was in der aktuellen Funktion grade nicht von Belang ist. Grade wenn du später andere ModelLoader schreibst, gibts doch sicherlich einige Dinge, die wiederholt werden. Ich kann mir vorstellen, dass das Laden von Modellen oft nach einem ähnlichen Schema abläuft - da würde dann eventuell das "Template Method Pattern" greifen.

    nach meiner Vorstellung einer Klasse (Schablone für etwas mehrfach Instanziierbares)

    Die Vorstellung ist nur bedingt richtig, siehe oben.



  • Hallo,

    Okay, das hilft mir alles schon etwas weiter. 🙂

    @pumuckl:

    Also Singleton ist ja aus vielerlei Gründen verpöhnt und ändert ja auch nur, dass man nicht mehrere Instanzen anlegen kann, während mich ja eher die einzige Instanz als logische Implikation stört. Aber wenn das nicht so schlimm ist oder es keine "Entartung" einer Klasse bedeutet, bin ich bereits zufrieden.

    "Single Responsibility Pattern" gefällt mir mit der Erklärung "nur ein Änderungsgrund" sehr gut. Allerdings ist das ne Frage der Formulierung. "Es hat sich etwas am Modellformat" geändert, ist der einzige Grund. "Es hat sich etwas an der Vertexdarstellung" geändert wäre aber einer von etwa zehn Gründen. Jetzt stehe ich vor der Wahl, entweder alles so zu belassen oder 10 kleinere Klassen zu machen. Was meinst Du mit wegkapseln? Methoden nehmen, in eine Klasse stecken und dann halt in meiner Loaderklasse für diese einzelnen "Aufgabenbereiche" wieder jeweils eine Klasse haben? Singletons sind global, meine "Subklassen" sollten aber Teile der Loaderklasse sein...

    Grade wenn du später andere ModelLoader schreibst, gibts doch sicherlich einige Dinge, die wiederholt werden. Ich kann mir vorstellen, dass das Laden von Modellen oft nach einem ähnlichen Schema abläuft - da würde dann eventuell das "Template Method Pattern" greifen.

    Sieht interessant aus, könnte vll. hilfreich sein. Aber verschiedene Ausprägungen der Sub-Algorithmen benötigen evtl. verschiedene Parameter. Bei meinem aktuellen Design haben meine Methoden ja die Freiheit, die kompletten Interna des neu zu erstellenden Models einsehen und setzen zu können. Hat Vor- und Nachteile, ich werde das Mal überdenken, danke. 🙂

    Jetzt schlägt Decimad eher freie Funktionen vor, Du hast aber an meinem Klassenansatz nichts kritisiert. Gibt es irgendwo Material oder Denkanstöße, wann ich in einem Fall wie meinem freie Funktionen und wann eine Kapselung durch Klassen lösen könnte? Freie Funktionen können anscheinend ja auch kapseln, indem ich anonyme namespaces nutze. Meine Vererbungsidee ist dann natürlich passé.

    @Decimad:
    Ok, ich kannte anonyme namespaces bis eben gar nicht. Das sieht praktisch aus.

    Den Aufruf dieser Konvertierungsfunktion könntest du dann beispielsweise in Objekten, abgelitten von einer abstrakten Model-Converter-Klasse, erledigen, welche zudem noch die Dateinamenerweiterungen etc. überprüfen könnte und was weißt ich nicht und dann von einer Model-Loader-Registry dazu benutzt wird, um kontrete Modelle zu laden und in deinem Format auszuspucken

    Okay, hier kann ich nicht ganz folgen. Ich versteh deine Idee so weit so: Die Model-Loader-Registry hat die ganzen ModelLoader, welche von einer abstrakten Basisklasse erben. Jetzt sollen die einzelnen ModelLoader eine Konvertierungsfunktion aufrufen, welche dann wiederum frei ist. Dann versteh ich aber nicht die ModelLoader-Klasse, welchen Sinn hat diese dann?

    Und als ein Leitsatz unter der Beobachtung, dass das, was du da hast, bereits gut funktioniert: Was im Moment gut funktioniert ist praktisch immer besser als das was eigentlich besser wäre

    Vll. bin ich zu wenig zielorientiert und zu sehr nach einem Design versessen, dass extrem sauber ist. Ich kann ja Mal einfach posten, was ich gerade habe und vll sieht man dann schon was.

    Also meine Basisklasse hat folgende Definition:

    class AssimpModelLoader;
    
    	class DLL_SPECIFIER ModelLoader
    	{
    	private:
    		typedef AssimpModelLoader*	AssimpModelLoaderPtr;
    
    		ModelLoader(const ModelLoader&);
    
    	protected:
    		// Methods for Manipulation of new Model
    		Model::MeshHolderVector&		GetMeshHolders(Model& model)	{return model.meshHolders_;}
    		Mesh&							GetMesh(Model& model)			{return model.aggregatedMesh_;}
    		Model::MaterialMap&				GetMaterialMap(Model& model)	{return model.materialMap_;}
    		Model::TextureMap&				GetTextureMap(Model& model)		{return model.textureMap_;}
    		Model::AnimationMap&			GetAnimationMap(Model& model)	{return model.animations_;}
    		Model::NodeVector&				GetNodeVector(Model& model)		{return model.nodes_;}
    		Model::BoundingSphereVector&	GetBoundingSphereVector(Model& model) {return model.boundingSphereTree_;}
    
    	public:
    		ModelLoader() : assimpModelLoader_(0) {}
    
    		virtual Model*	LoadModel(const std::string& filename);
    
    		virtual ~ModelLoader();
    
    	private:
    		AssimpModelLoaderPtr assimpModelLoader_;
    	};
    

    D.h. sie hält von jedem Loader jeweils eine Instanz. Möchte der Benutzer jetzt einen Loader verwenden, ruft er von der einen Instanz dieser Basisklasse (die übrigens nicht abstrakt ist, sorry), die LoadModel-Methode auf:

    Model* ModelLoader::LoadModel(const string& filename)
    	{
    		string::size_type pos = filename.find_last_of('.');
    		if(pos != string::npos)
    		{
    			string ending(filename.substr(pos + 1));
    
    			if(ending == "dae" || ending == "obj" || ending == "3ds" || ending == "x")
    			{
    				if(!assimpModelLoader_)
    					assimpModelLoader_ = new AssimpModelLoader;
    				return assimpModelLoader_->LoadModel(filename);
    			}
    			else
    				THROWEXCEPTION(NoSuitableModelLoader, "No suitable model loader could be found.");
    		}
    		else
    			THROWEXCEPTION(FileNotFound, "Filename " + filename + " is invalid.");
    	}
    

    Die ist schlau und guckt sich die Dateiendung an, um dann den Aufruf an die korrekte Ladeklasse weiterzuleiten. Es gibt z.Z. nur den Assimp-Loader.

    Und der Assimp-Loader, der also das eigentliche Laden übernimmt, macht jetzt die ganze Arbeit, wo ich dann zu dem eigentlichen Designproblem komme, dass ich nicht sicher bin, wie ich genau weiter kapsele.

    Diese Lösung in freie Funktionen umzubauen halte ich für schwierig und empfinde ich auch nicht so ganz als beste Lösung...



  • Die Model-Loader klasse hat das einheitliche Interface, kann die Datei-Erweiterungen checken etc. Die Registry fragt dann die Loader ab, welcher meint, eine bestimmte Datei lesen zu können. Das ganze hat den Vorteil, dass du immernoch die freie Funktion hast... (Also cut&paste in ein anderes projekt, wo du nur mal fix die datei laden willst) und in deinem aktuellen Projekt den ganzen automatischen Firlefanz hast.
    Dein if( ext == ".dae" ) return new ModelLoader()->LoadModel();
    ist mir natürlich jetzt wirklich etwas unschön aufgefallen 😉 Warum kein temporäres lokales Objekt?



  • Wenn ich 10 Modelle nacheinander lade, erstelle und zerstöre ich 10 Mal Instanzen.

    Und bei deiner Lösung habe ich jetzt nochmal eine Klasse mehr, oder? Aber den Vorteil, eine freie Funktion zu haben, finde ich jetzt so eine Sache. Die freie Funktion wäre für mich ja eine Alternative zu Klassen. Beides zu haben wirkt für mich nicht mehr so attraktiv.



  • Also welche beider Optionen ergibt denn hier mehr Sinn:

    // #1
    Model* m1 = ReadModelDae( "some.dae" );
    
    // #2
    DaeModelReader reader;
    Model* m2 = reader.Read( "some.dae" );
    

    Und anschließend würde ich mir sowas vorstellen:

    /*
       Hier könnte man natürlich mit entsprechenden Handler-Objekten noch mehr machen, als das nur auf die Datei-Endung festzulegen.
    */
    LoaderRegistry::Get().Register( "dae", ReadModelDae ); 
    LoaderRegistry::Get().Register( "3ds", ReadModel3ds );
    
    Model* m = LoaderRegistry::Get().Load( "myfile.3ds" );
    


  • Achso, der Vorteil an deiner Lösung ist, dass man dynamisch ModelLoader an Endungen mappen kann, sag das doch.

    Ansonsten ist #2 ja bereits implementiert wie oben beschrieben. Aber ein hartes Verdrahten von Endungen zu bestimmten Loadern reicht auch aus.

    Aber das Ganze hat jetzt nichts mit meiner Frage nach der internen Gliederung zu tun, oder? Mir ging es nur darum, mein Modell zu zeigen, um so was wie reine freie Funktionen auszuschließen, da ich ja irgendwo wohl Klassen brauche (nach meinem aktuellen Stand; wobei man das alles natürlich auch ohne Klassen simulieren könnte).



  • Ich seh das halt im Moment andersherum irgendwie: Du simulierst mit Objekten das Verhalten von freien Funktionen 😉



  • Also wenn ich freie Funktionen hätte, müsste jede Funktion zig Parameter erhalten, die ich ansonsten während der Ladezeit in einer Klasse halte. Zudem brauche ich u.U. ja noch andere Bibliotheken wie die externe Klasse der Assimp-Bibliothek. Auch hier gibt es eine Instanz, die erstellt und gespeichert werden muss. Das sind für mich schon Argumente für ne Klasse.

    Außerdem haben alle deine Beispiele ebenfalls Klassen. Ich verstehe nicht ganz, worauf Du hinaus willst.



  • Man kann solche x-tausend Parameter auch zu einem Kontext zusammenfassen (also beispielsweise einer struktur, die alle algorithmus-relevanten daten für alle zwischenschritte vorhält).

    Ob man das nun über einen Kontext mit freien Teilhilfsfunktionen oder über eine private Klasse mit Hilfsmemberfunktionen macht, ist ja letztlich egal, aber davon sollte nichts nach außen (zum Anwender) gelangen. Dass sich der Anwender selbst ein Loader-Objekt erstellen muss um ein Objekt zu laden, ist halt ein nach außen gewandertes Implementierungsdetail.

    Der Benutzer hat einen Dateinamen und will ein Modell davon erstellt haben. Das simpelste für ihn überhaupt ist folgende Signatur:

    Model* LoadDae( std::wstring& filename );
    

    Das hindert dich natürlich in keiner Weise, innerhalb von Load ein privates Helferobjekt mit Hilfsmethoden, zwischenspeichern etc. zu benutzen, oder ein Kontext-objekt mit freien Hilfsfunktionen zu füllen/verändern.

    Edit:
    Ich möchte ja gar nicht sagen: Benutze keine Klassen. Aber mir als Dau würde kein Grund daf+r einfallen, ein Objekt zu erstellen, dass mir nur eine einzige Aufgabe erfüllt, nämlich eine Datei zu laden und ein Modell auszuspucken. Ich denke mir: zwischen dem Laden 2er Dateien besteht doch eigentlich gar kein Zusammenhang, also kann ich ja jedes Mal eine andere Instanz davon benutzen. Danach würde ich mir überlegen: Hält das Objekt vor oder nach dem Aufruf von der Methode Load irgendwelche wichtigen Daten?



  • Ne, vorher und nachher gibt es da wenig. Aber wie regele ich den Zugriff auf die Interna der Klasse? Vorher ging einfach ein praktisches friend auf die ganze ModelLoader-Klasse. Jetzt müsste ich für jede Methode, die irgendwo irgendwas lädt ein extra friend einbauen.

    Dass ich vorher und nachher keine wichtigen Daten mehr erhalte, ist aber insofern kein Argument gegen eine Klasse, da das Template Method Pattern ja vorher und/oder nachher auch nichts mehr hält. Also ist es anscheinend ok, durch Klassen Konzepte umzusetzen. Und hier setze ich eben das Konzept um, dass abgeleitete Klassen die Möglichkeit erhalten, Interna der Klasse zu ändern, d.h. ich vererbe eine Verantwortung.



  • Ich sehe im Moment nicht den Zusammenhang mit dem Template-Method-Pattern oder Objekten darin.

    Ich würde genau wie du das Laden eines Modells auslagern, wenn mehrere Formate im Spiel sind. Heute hatte ich die Idee, sozusagen eine BareModel-Struktur zu definieren, die alle Nutzdaten für ein Modell enthält (alles öffentlich, die algorithmen/teilfunktionen können gut dram rumfuhrwerken) und dann zum Konstruieren eines echten Modells hergenommen werden kann. Irgendwie sowas:

    struct ModelData {
        std::vector< Point > vertices;
        std::vector< Vector > normals;
        std::vector< int > indices;
        // ...    
    };
    
    struct DaeModel {
        // ...
    };
    
    void ConversionPart1( ModelData& to, DaeModel& from );
    void ConversionPart2( ModelData& to, DaeModel& from );
    
    Model* LoadDae( const std::wstring& file )
    {
         DaeModel dae( file );
         ModelData md;
    
         ConversionPart1( md, dae );
         ConversionPart2( md, dae );
    
         return new Model( md );  // Hier könnte man auch std::move verwenden^^
    }
    


  • Das Template-Method-Pattern spiegelt in meinen Augen wiederum einen Algorithmus und damit vom Sinn her eher eine Funktion wieder. Wenn man deine Frage, ob vorher oder nachher wichtige Daten in der Klasse sind, auf ein Template-Method-Pattern, umgesetzt durch Klassen, anwenden, käme man auch zu dem Ergebnis, dass hier eine Klasse sinnlos wäre. Und mehr Aufgaben als den Algorithmus sollten entsprechende Klassen auch nicht haben auf Grund des Single-Responsible-Principles, richtig?

    Bei deiner Lösung würde ich die zentralen Elemente immer durch eine zusätzliche Indirektion ansprechen müssen, das empfinde ich für das Design des Models auch nicht unbedingt als schön, es bläht auch Zugriffsausdrücke auf. Auch wenn mir die Idee sonst gefällt. Was ist DaeModel dann für eine struct, was enthält sie und wie soll sie funktionieren?

    Eigentlich fand ich meine protected Zugriffsmethoden und die Idee mit der Vererbung der Zugriffsmöglichkeit auf private Methoden sogar sehr elegant.



  • Ich verstehe das Template Method Pattern irgendwie anders, aber ist ja eigentlich auch nicht so wichtig.

    DaeModel ist halt das, was der normale Collada-Loader da so ausspucken würde, den du ja wahrscheinlich verwendest, die Daten, die du anschließend konvertieren möchtest.

    Klar, meine Funktionen brauchen jetzt natürlich immer den Kontext mitgereicht. Aber man sieht der Sache auch sofort an, dass man sie in eine Klasse umwandeln kann (das wäre sozusagen immer der this-Zeiger).

    Die äquivalente Sache mit Klassen:

    struct ModelData {
        // ....
    };
    
    namespace {
        class DaeConverter : public ModelData {
        public:
            Converter( DaeModel& d ) : d_(d) {
                Step1();
                Step2();
                Setp3();
            }
    
        private:
            void Step1() {}
            void Step2() {}
            void Step3() {}
        };
    }
    
    Model* LoadDae( const std::wstring& file )
    {
         DaeModel dae( file );
         return new Model( DaeConverter( dae ) );
    }
    

    Wohlgemerkt ändert sich für den Benutzer von LoadDae überhaupt nichts!



  • Hi,

    Nagut, ich nutze wie gesagt eine Bibliothek, die ganz viele Formate lädt, aber das ist ja erstmal egal. Deine Lösung weicht sowieso nur in "Kleinigkeiten" von meiner ab, die Klassenversion sieht sogar genau so aus. Vll. sieht man das eine oder andere besser, aber ich glaube, bei dem Diskussionspunkt kommt jetzt nicht mehr so viel rum.

    Hast Du denn ne Idee, wie ich jetzt gut einige Funktionen, die gut zusammenpassen, auslagern könnte? Gut, ich könnte das in verschiedene Dateien stecken. Ich weiß nicht, ob das irgendwie hilft. Oder ich mache das einfach über Kommentare:

    // Mesh
    void ConvertVertices(...);
    void ConvertIndices(...);
    void ConvertNormals(...);
    
    // Animations
    void ConvertBones();
    void ConvertNodes();
    void ConvertAnimations();
    
    // Collision
    void IntegrateSpheres();
    

    Na ja... ein großer Haufen an Funktionen ist vermutlich genau so übersichtlich. Man schaut sich die Liste an und sieht, was so getan wird. Ob eine Funktion eigentlich einer anderen unterzuordnen wäre... wayne?

    pumuckl:
    Auch wenn Du vermutlich nicht mehr mitliest, würden mich die Antworten auf meine Fragen trotzdem noch sehr interessieren. 🙂



  • Naja, diese erste Stufe der Aufteilung lässt sich natürlich eine weiteres Mal durchführen! Aber am Ende wird der Quellcode und die Funktion irgendwo stehen müssen, weiß nicht, ob es da Sinn ergibt, den zugehörigen Quellcode, der niemnanden außer dem Implementierer der Konversions-Bibliothek interessiert, auch noch auf mehrere Klassen zu verteilen und irgendwie schick zu modularisieren.

    Die Sache ist die und der Umstand ist der. Du schreibst gerade eine Bibliothek und versuchst dir den Job so angenehm wie möglich zu machen. Das ist legitim, und so denke ich auch 😉 Aber davon soll doch der Benutzer nichts mitbekommen! Ich will als Benutzer von deiner Entscheidung eigentlich nix sehen, dass du C++-Klassen verwendest, um dir das explizite Dereferenzieren zu sparen! Wenn du es dann doch irgendwann anders machen möchtest, muss all der benutzende Quellcode verändert und neu kompiliert werden, statt nur gelinkt.



  • Zu diesem einen Punkt muss ich ja sagen, dass der Aufruf in Wahrheit schon durch eine freie Methode geschieht, die eben so ein statisches Objekt meiner Loader-Klasse hält. Austauschbarkeit von Klassen durch freie Funktionen ist also unproblematisch.

    Also ich habe ein paar Eindrücke hier im Thread gewonnen und danke Dir auf alle Fälle für Deine ganze Zeit! Du hast hier ja wirklich Mal richtig Zeit aufgewendet. 🙂

    Leider ist mein Grundproblem (also unabhängig von diesem einen Thema hier) immer noch da: Ich sehe viele Möglichkeiten, mir fällt es aber nach wie vor schwer zu sehen, was auf lange Sicht besser wartbar und erweiterbar ist. Und was die Übersichtlichkeit angeht... ich habe das Gefühl, es ist unübersichtlich ganz gleich wie ich es aufbaue. Vll. sind Grafik-Engines auch zu komplex, um sie richtig schön zu machen (wenn man so von WoW oder was das war hört...)

    Auf alle Fälle würde ich mich in dem Punkt gerne stark weiter entwickeln. Gibt es nicht irgendwo irgendein gutes Buch dazu? Design Pattern a la Gang of Four angucken find ich auch immer so bla. Erstens bietet C++ viel mehr Möglichkeiten und meistens gibt es oft auch elegante Lösungen ohne Pattern.

    Ich bräuchte mehr so Antworten auf die Fragen "wann ist es gut eine Klasse zu nehmen, wann nicht?", "Vererbung vs. Komposition/Aggregation", "Refactoring" uvm. und das auf C++-Ebene, da orientieren sich viele Bücher zu sehr an reinem OOP wie in Java und vernachlässigen freie Funktionen, statische Polymorphie, Funktionszeiger...

    Gibt es so was? Oder muss ich einfach Erfahrung sammeln bis zum umkippen? Aber wie so oft im Leben, habe ich das Gefühl zu wenig aus Fehlern zu lernen oder die zu wenig zu verstehen oder die Erkenntnisse zu vergessen. Ich bin in diesem Forum ja auch schon seit etwa 200 Jahren (mit andrem Nick damals) und fühl mich immer noch wie ein blutiger Anfänger. 😞



  • Also wenn du das auch alles hinter einer Funktion versteckst und der Rest deines Projekts vollkommen unabhängig von der Implementierung hinter dieser Funktion ist, dann ist das Ziel doch erreicht, finde ich!
    Dabei ist doch auch schon eine Menge erlernt, man kann Klassen verwenden, wo es einem hilft, aber wenn es sich streng genommen um keine richtigen Objekte handelt, sollte man sich dazuschreiben, warum man sie verwendet, damit man das in 2 Jahren noch versteht und das ganze nicht nach außen kommunizieren.
    Ein (zumindest nicht durch einen äußeren Zustand paramatrisierbarer) Algorithmus hat halt nix von einem Objekt irgendwie.
    Sowas wie

    std::find< std::vector<int>::iterator > finder;
    std::vector<int>::iterator res = finder.find( vec.begin(), vec.end(), val );
    

    ergibt halt aus objektorientierter Sicht vielleicht nicht allzu viel Sinn, aber du hast gelernt, dass es einem intern Arbeit ersparen kann.

    Wir sind uns da ganz ähnlich in dem, was du im letzten Abschnitt erläuterst. Aber sieh's mal so: ein Mathe-Buch versteht man auch nicht unbedingt dadurch, dass man es sich einmal durchliest. Hinterher muss man (zumindest ich, es mag da ja Genies geben) das alles fünfmal anwenden und dann das Buch nochmal durchlesen. Ich denke nicht, dass es die Lernkurve positiv beeinflusst, wenn man auf Anhieb alles perfekt machen will. Denn dann hängt man sich an Details auf und vergibt die Chance, durch die bloße Masse in anderen Gebieten die Erfahrung zu sammeln, um das aktuelle Problem besser zu lösen.

    Ich denke zudem nicht, dass man sich einen Informatik-Studenten hernehmen kann, der malt sich ein paar UML-Diagramme und schreibt dann eine super Engine from scratch. Die Leute, die das machen oder gemacht haben (zumindest die grobe Architektur), haben auch vorher schonmal eine schlechtere Engine geschrieben und davor noch eine schlechtere.

    Du hast einen funktionierenden Model-Loader! Kümmer dich erstmal um wichtigeres als darum, ihn zu versuchen perfekt zu machen, denn das wird er nie 😉



  • Okay, danke, dann sollte ich das wohl tun, wobei ich auch anmerken muss, dass ich Entwicklung und Diskussion über Design ja immer parallel führe. 🙂

    Falls doch noch irgendwer Ideen oder eine Buchempfehlung hat, freue ich mich.


Anmelden zum Antworten