Verständnisfrage



  • dot schrieb:

    [...] -> Refactoring.

    Irgendwie sehe ich da jetzt keinen Widerspruch zu meinem Geschreibsel.
    Außer evtl. dass du ein klassisches, statisches OO Klassenkonzept nicht mal eben "Refactorst".



  • xaoC schrieb:

    Außer evtl. dass du ein klassisches, statisches OO Klassenkonzept nicht mal eben "Refactorst".

    Was genau verstehst du unter einem "klassischen, statischen OO Klassenkonzept"?



  • Ein hauptsächlich auf Vererbung basierendes Konzept, welches Eigenschaften und Verhaltensweisen in Klassen oder Strukturen presst.

    Beispielsweise die grundsätzliche Trennung von grafischen Eigenschaften (Draw) und nicht grafischen Eigenschaften (Update Only) in getrennten Klassen, womöglich nochmals abgeleitet von einem abstrakten "WorldEntity" oder so ähnlich.

    Was ist z.B. ein BoundingVolume? Es hat eine WorldPosition, eine Rotation u.s.w., aber eigentlich keine Zeichenfunktionalität. Später würdest du gerne mal alle Boundingvolumes zu Debug Zwecken per Draw ausgeben, aber deine Klassenhierarchie verbietet dir das. Deine Logik bzgl. BoundingVolumes baut mittlerweile auf "WorldEntity" o.ä. auf, Refactoring bedeutet also: Du hast ein grundsätzliches Problem, was sich durch den gesamten Code zieht.

    Umbauen ist evtl. nicht nur zeitaufwendig, sondern auch gefährlich, weil du getesteten Code quasi wieder neu validieren musst.

    In meinen Augen hört bei sowas das statische Design auf, praktikabel zu sein.
    Wenn ich die Zeit hätte, würde ich versuchen, ein kleines, aber sauberes OO Konzept zu designen, mit welchem ich dynamisch auf Objektebene zur Laufzeit Eigenschaften und Verhaltensweisen definieren kann.

    Natürlich habe ich immer noch einen OO Entwurf, aber um meine Objekte zu erweitern. Ich presse aber keine Funktionalität in statische Klassen.

    Jetzt klarer was ich meine?



  • Ja, mir ist jetzt klar was du meinst. Was du nennst sind aber eben alles Beispiele dafür, wie man OOP nicht betreiben sollte. Die Tatsache, dass man OOP falsch verstehen und falsch machen kann, bedeutet nicht, dass OOP prinzipiell fehlgeleitet ist und gemieden werden muss. Einem Boundingvolume eine "Zeichenfunktionalität" hinzuzufügen, verletzt beispielsweise eines der Grundprinzipien der OOP, nämlich das Single Responsibility Principle. Wenn deine Klassenhierarchie irgendwas "verbietet", dann passt deine momentane Abstraktion eben offenbar nichtmehr mit deinem momentanen Problem zusammen und muss dementsprechend überdacht werden. Wenn die notwendigen Änderungen sich durch den ganzen Code ziehen, dann ist das eben so. Was wäre die Alternative? Früher oder später musst du refactoren, ansonsten werden die Probleme sicher nicht weniger werden...

    Wie gesagt, all diese Programmierstile ergänzen sich wunderbar. OOP ist nur ein (durchaus wichtiges) Werkzeug von vielen. Und in der OOP geht es ganz sicher nicht darum, wer mehr Design Pattern in sein Projekt zu stopfen vermag. Um das Beispiel mit dem Bounding Volume nochmal aufzugreifen: Ein Bounding Volume sollte z.B. in der Regel vermutlich überhaupt einfach nur ein normaler Werttyp sein, fernab jeglicher OOP. Das heißt nicht, dass das komplette System auf einer höheren Ebene nicht nach OOP Prinzipien designed werden darf. Und man kann OOP wunderbar verwenden, um ein System zu bauen, welches data driven ist...



  • dot schrieb:

    Ja, mir ist jetzt klar was du meinst. Was du nennst sind aber eben alles Beispiele wie man OOP nicht betreiben sollte. Die Tatsache, dass man OOP falsch verstehen und falsch machen kann, bedeutet nicht, dass OOP prinzipiell fehlgeleitet ist und gemieden werden muss. Einem Boundingvolume eine "Zeichenfunktionalität" hinzuzufügen, verletzt beispielsweise eines der Grundprinzipien der OOP, nämlich das Single Responsibility Principle. Wenn deine Klassenhierarchie irgendwas "verbietet", dann passt deine momentane Abstraktion eben offenbar nichtmehr mit deinem momentanen Problem zusammen und muss dementsprechend überdacht werden. Wenn die notwendige Änderung sich durch den ganzen Code zieht, dann ist das eben so. Früher oder später musst du refactoren, ansonsten werden die Probleme sicher nicht weniger werden...

    Wie gesagt, all diese Programmierstile ergänzen sich wunderbar. OOP ist nur ein (durchaus wichtiges) Werkzeug von vielen.

    Korrekt.
    Letztendlich braucht's Erfahrung, um eben jene Single Responsibilities zu erkennen, zu erkennen ob der Entwurf generell Sinn macht oder man sich eines anderen Ansatzes bedienen muss.

    Deswegen macht es IMO keinen Sinn, komplexe Designs zu entwerfen, ohne dabei konkrete Erfahrungswerte im Hinterkopf zu haben. Eben jene Erfahrungswerte sammeln wir zur Zeit. Und wir haben schon viel refactored. Naja, man wächst an seinen Fehlern.



  • Kleine Zwischenfrage, die mir beim Mitlesen so eingefallen ist: Auch wenn es natürlich vom ganzen Rest abhängt, aber würdet ihr es eventuell für sinnvoll halten, den DoD beim fahrbaren Bauhof so zu lösen, dass man von Anfang an gar nicht zwischen Gebäude und Fahrzeug unterscheidet, sondern Fähigkeiten wie Fahren, Ballern usw. als decorator an seine Objekte hängt, die dann commands verarbeiten können oder eben nicht? Welche gerade dranhängen könnte man z.B. über states regeln.



  • Ohne weitere Infos, würde ich den fahrenden Bauhof vermutlich als zwei verschiedene Einheiten modellieren, die ineinander umwandelbar sind.



  • Wie meinste das? Bei sowas fällt mir gerade nix ein, wie das mit dem Umwandeln sinnvoll ginge.

    class Gebaeude{};
    class Fahrzeug{};
    class StehenderBauhof : public Gebaeude {};
    class FahrenderBauhof : public Fahrzeug {};
    void Spiel() {
        vector<Gebaeude*> rumsteher;
        vector<Fahrzeug*> herumfahrer;
    }
    

    Hattest du sowas im Kopf?

    class Einheit{};
    class Fahrzeug{};
    class BauhofState{};
    class BauhofStandingState : public BauhofState{};
    class BauhofDrivingState : public BauhofState{};
    class Bauhof : public Einheit {
        BauhofState* state_;
    };
    

  • Mod

    dot schrieb:

    xaoC schrieb:

    Man ist sich nicht über Verantwortlichkeiten im Klaren, "Kommunikation" zwischen Objekten wird letztendlich über Delegates o.ä. Prinzipien abgefackelt und da du i.d.R. später bestimmten Klassen mehr Funktionalitäten einräumst als jemals geplant waren, endet das Ganze bei uns momentan in einer recht monolitischen Sprite Klasse und einem Rahmensystem (Screens etc.), welche untereinander per Delegates Kommunizieren.

    Klingt mir so, als wär das Design kaputt und ein Refactoring dringend notwendig.

    das klingt fuer mich wie ein missbrauch von 'refactoring'.
    refactoring sollte ein design unterstuetzen und verbessern (im rahmen des designs).

    wenn ein design kaputt ist, braucht man ein redesign. dabei sollte man sich die grundlegene konzepte anchauen und nicht nur die architektur bestehender konzepte und sie alternieren.

    wenn man es mal auf was anderes uebertraegt z.b. ein haus, wenn das design "kaputt" ist wie du sagst, hilft renovieren/refactoring nichts, das design bleibt (z.B. member in andere klassen schieben oder klassen unterteilen/mergen. natuerlich spreche ich fuer 'meistens' und nicht absolut fuer 'das ist in 100% der faelle so').
    wenn man den designentwurf behaelt, und es einfach nochmal baut und versucht darauf zu achten nicht dieselben problemstellen herzustellen, ist das design dennoch kaputt, es wird nur verdeckt. eventuell laeuft es gut, eventuell laeuft man aber auch wieder in design spezifische probleme.

    ich stimme dir zu, DDS ist keine substitution fuer OOP, es sind verschiedene dinge. das eine ist die art die architektur zu designen, das andere ist das mittel wie man das design verwirklicht/programmiert. gerade deswegen sollte man sich von der objekt orientierten idee nicht das design vorgeben lassen, z.b. dass daten und funktionalitaet zusammenhaengen muss, denn dem ist nicht so und oft eine quelle von fehldesign wenn man sich auf idealisiertes OOP verlaesst.
    natuerlich ist idealisiertes DDS (wie ich schon schrieb im ersten beitrag) eine naive sache und ebenfalls nicht problemfrei, man kann zwar mit wirklich idealem DDS alles erstellen, aber meistens bekommt man dann starke performance probleme da eben daten zur laufzeit evaluiert werden und nicht an funktionalitaet gebunden sind.

    ich denke OOP und DDS zusammen kann sehr gut ineinander greifen, aber genausogut kann man DDS und strukturiertes c schreiben. ich wollte im ersten post ausdruecken, dass DDS prinzipien sich heutzutage durchsetzen, als gegengewicht zu dem idealisierten OOP (mit impliziertem design), nicht dass es OOP ersetzt, sorry falls das falsch rueberkam.

    ich habe das hier schon paar mal gepostet, aber wenn man ein wenig programmieren kann, ist das sicherlich mal unterhaltsam (und lehrreich) den vortrag zu lesen:
    http://macton.smugmug.com/gallery/8936708_T6zQX#!i=593426709&k=ZX4pZ

    the right tool, for the right job.


  • Mod

    Dobi schrieb:

    Wie meinste das? Bei sowas fällt mir gerade nix ein, wie das mit dem Umwandeln sinnvoll ginge.

    class Gebaeude{};
    class Fahrzeug{};
    class StehenderBauhof : public Gebaeude {};
    class FahrenderBauhof : public Fahrzeug {};
    void Spiel() {
        vector<Gebaeude*> rumsteher;
        vector<Fahrzeug*> herumfahrer;
    }
    

    Hattest du sowas im Kopf?

    class Einheit{};
    class Fahrzeug{};
    class BauhofState{};
    class BauhofStandingState : public BauhofState{};
    class BauhofDrivingState : public BauhofState{};
    class Bauhof : public Einheit {
        BauhofState* state_;
    };
    

    ich wueste nicht wieso man zwei klassen braucht, ob ein bauhof sich bewegt oder steht, ist eine eigenschaft, keine funktionalitaet.

    pEntity->MaxSpeed(...)
    

    fertig.

    du willst ja am ende nicht zig mal durch objekte iterieren und pro eigenschaft einen container mit objekt referencen haben, sondern einmal ein
    for_each(...)
    pObject->Update();
    aufrufen.

    ansonsten muestest du ja, wenn ein bauhof waffen bekommt und sich verteidigen kann, auch ein objekt machen in das du umwandelst, bzw hast du dann 4 objekte
    static-nodefense
    static-defense
    moving-nodefense
    moving-defense

    oder wie entscheidet man ob eine eigenschaft ein neues objekt braucht?

    wenn ein bauhof ein wirklich besonderes objekt ist, weil es z.b. andere objekte erstellen kann, dann wuerde es meiner meinung nach eine spezialisierung legitimieren, weil es zwar nicht unmoeglich ist jedem objekt eine liste mit objekten zu geben (als property) die es erstellen kann, aber in n:1 faellen waere das vermutlich unsinnig. (ein fall wo DDS zwar funktioniert, aber so over engineered ist wie ein idealisiertes OOP-design).
    vielleicht erstellt man im laufe des spieles eine werft und eine space station, die beide einheiten erstellen koennen, die wuerde ich vom selben "bauhof" objekt instanzieren, aber dann wieder keine neue klasse, da sie zwar andere objekte erstellen (was eher eine eigenschaft ist), aber die selbe funktionalitaet haben.



  • Das oben war ja auch nur eine Frage, um Dot's Vorschlag mit dem sich-umwandeln-können zu verstehen. Den State fände ich da schon schöner, und darüber könnte man dann ja auch dein "pEntity->MaxSpeed(...)" haben. Die Anfrage würde dann vom Objekt an den State weitergeleitet, den man einfach austauschen könnte. Man könnte aber auch natürlich direkt die Eigenschaften ändern anstatt sie im State zu verstecken, dann halt eher data driven. Mhh, das hört sich für mich so an als könnte man mit States nen Übergang zwischen OOP- und DDP-Lösungen haben bzw. kombinieren, weil man die Datenänderungen darin kapseln könnte, oder ist das sinnlos? 🙂

    Edit: Vermutlich laber ich gerade Quatsch. Ich versuche nur zu verstehen, wie sowas data driven funktioniert, weil ich das vorher noch nie gehört hatte.


Anmelden zum Antworten