Virtueller Dummy?



  • Badestrand schrieb:

    Dann gehört das auch nicht nach UIElement

    Hmm, aber wie soll ich die Methoden dann von einem Basisklassenzeiger aus aufrufen?

    👎 😕



  • cpp@loggedoff@loggedoff schrieb:

    Badestrand schrieb:

    Dann gehört das auch nicht nach UIElement

    Hmm, aber wie soll ich die Methoden dann von einem Basisklassenzeiger aus aufrufen?

    👎 😕

    Indem du eine andere Basisklasse definierst und dann halt von der erbst.

    Wenn du das sowieso nur auf Panel anwenden kannst, musst du das auch nicht abstrahieren. Das bringt dann ja nichts.



  • @drakon
    Ich verstehe nicht..

    Es könnten ja noch viel mehr verschiedene Elemente werden. Jedes Element hat eigene Methoden, alle Methoden eines jeden Elements sollen aber über einen Basisklassenzeiger aufrufbar sein...

    MfG



  • cpp@loggedoff@loggedoff schrieb:

    @drakon
    Ich verstehe nicht..

    Es könnten ja noch viel mehr verschiedene Elemente werden. Jedes Element hat eigene Methoden, alle Methoden eines jeden Elements sollen aber über einen Basisklassenzeiger aufrufbar sein...

    MfG

    Ja. Aber die Frage ist, ob das Sinn macht.
    Sagen wir mal, dass du einen Container von Objekten hast mit den Basisklassenzeigern und du jetzt gerne eine Funktion aufrufen willst, die genau 1 Objekt da drin wirklich hat. Wie findest du dann raus, welches das ist?
    Eine gemeinsame Basisklasse macht nur Sinn, wenn sie ein Konzept verkörpert. Natürlich kannst du bei allen anderen Objekten eine leere Funktion einbauen, aber wirklich elegant ist das nicht.



  • Öh, nun ja, ob es Sinn macht 🙄
    Ich glaube da ist dynamic_cast<>() mein Freund... hatte den schon wieder vergessen...

    for(UIElementSetIt i = elements.begin(); i != elements.end(); ++i)
        if((*i)->GetType() == UIElement::Type::StatusBar)
            dynamic_cast<StatusBar*>(*i)->SetValue(id, value);
            //(*i)->SetValue(id, value);
    

    Somit brauche ich "SetValue()" in der Basisklasse nicht mehr.. hmm.. ich sollte mal Grundlagen auffrischen 🙄

    MfG


  • Administrator

    cpp@loggedoff@loggedoff schrieb:

    for(UIElementSetIt i = elements.begin(); i != elements.end(); ++i)
        if((*i)->GetType() == UIElement::Type::StatusBar)
            dynamic_cast<StatusBar*>(*i)->SetValue(id, value);
            //(*i)->SetValue(id, value);
    

    Ist das nicht ein wenig der Inbegriff für schlechtes Design? Du legst alle Objekte in einen Container, vergibst ihnen dafür einen gemeinsame Basisklasse, am Ende brauchst du aber jeweils den tatsächlichen Typ. Typisches Beispiel, zumindest meiner Meinung nach, wo der dynamic_cast auf ein schlechtes Design hinweist.

    Speichere die Objekte doch nicht in einem Container ab, sondern auf die Typen verschiedene Container, welche du brauchst! Man darf auch einen Zeiger mehrmals als verschiedener Typ abspeichern 😉

    Man kann eine Liste für die Childs haben. Aber auch eine Liste für die Statusbars. Eine Liste für Menüs. Eine Liste für Fenster mit Titeln. Usw. usf.
    Also besser mehr Container einführen und vielleicht mehr verschiedene Basisklassen, als probieren die MegaSuperDuperBasisklasse zu bauen, damit man nur einen Container verwenden muss.

    Grüssli



  • 😞

    Ja, ich habe versucht mit einem Container auszukommen. Liegt doch auf der Hand. Man muss nur einen Container durchlaufen, unterscheidet halt anhand einer Typ-Variablen (enum).

    Aber für jedes verschiedene Element einen eigenen Container? Daran hätte ich erst zuletzt gedacht... Wenn man wirklich viele verschiedene Elemente hat, muss das doch einfacher gehen?

    MfG


  • Administrator

    cpp@loggedoff@loggedoff schrieb:

    Wenn man wirklich viele verschiedene Elemente hat, muss das doch einfacher gehen?

    Es kommen immer die Elemente in einen Container, welche alle für das gleiche Verwendet werden sollen. Man sollte diese Gemeinsamkeit aber nicht herbeizwingen. Meistens sind es nicht wirklich viel mehr Container und man muss auch nicht mehr Container/Elemente durchlaufen, im Gegenteil, am Ende durchläuft man weniger Elemente, da man nur noch die durchläuft, welche man auch wirklich ansprechen möchte.

    Zudem fällt auch noch ein dynamic_cast weg, welcher auch "teuer" ist.

    Baue daher eine sinnvolle Klassenhirarchie auf, mit sinnvoll gewählten Basisklassen, welche jeweils nur eine Schnittstelle definieren.

    Grüssli



  • Ok danke, ich werd mich mit diesen Dingen beschäftigen.

    MfG



  • Da sich dein Design anscheinend mit GUI-Elementen befasst: Im Prinzip haben Buttons, Editboxen usw schon viele Gemeinsamkeiten, die meines Erachtens auch eine gemeinsame Basisklasse zulassen. Nur sollten z.B. die Button-Funktionen definitiv nicht aus der Oberklasse aufrufbar sein.

    Mir hilft es beim Design oft, mir die Dinge aus Benutzerseite zu überlegen. Also wie würde ich mit der perfekten GUI-Bibliothek programmieren? Das wird glaubich auch beim Extreme Programming angewandt, man nennt's wohl auch Top-Down-Programmieren. Oder so.

    Jedenfalls würde das bei mir etwa so aussehen: Ich suche mir spezielle Situationen aus, z.B. will ich ein Button zu einem Fenster hinzufügen oder den Text von einem Edit-Control abfragen.

    Window wnd;
    // ...
    
    // Button hinzufügen
    wnd.addItem( new Button( "Push Me", Koordinaten.., ... ) );
    
    // Text von Edit-Control abfragen
    EditCtrl* edit = wnd.getItem<EditCtrl>( ID_EDIT_BLUBB );
    std::cout << edit->getText() << std::endl;
    

    Irgendwie so, rein nach Intuition und persönlichem Schönheitsmaß. Daraus lässt sich dann natürlich schnell ein Design ableiten. Funktioniert bei mir jedenfalls recht gut, wäre bestimmt einen Versuch wert.



  • Jo danke, dieses Top-Down-Programmieren mache ich schon lange, ist zumindest bei kleineren Projekten echt genial.

    MfG



  • Es kann durchaus Sinn machen einer Basisklasse Funktionen zu verpassen die nicht in allen abgeleiteten Klassen sinnvoll implementiert werden können. Manche Designs können sonst schnell recht unübersichtlich werden.

    Schwierig ist wohl zu entscheiden wann welcher Nachteil/Vorteil überwiegt. Also "sauberes Design" ohne "optionale Methoden", oder geringere Komplexität durch Verwendung diverser "Tricks" als "optionale Methoden".

    Was man dann in den "nicht sinnvoll implementierbaren" Methoden macht ist natürlich die Frage.

    Angenommen (Beispiel) meine UIElement Klasse hat eine Funktion GetText und SetText. Wenn ich nun einen kleinen "Pfeil-Button" als UIElement implementiere, der keinen Text hat, was macht ich dann mit GetText und SetText? Die einfachste Variante ist hier wohl den Text 1:1 so zu behandeln als ob er relevant wäre. Wenn ich also SetText("foo") aufrufe sieht man zwar keine Änderung, aber GetText() wird mir danach wieder "foo" zurückliefern. -> Prinzip der geringsten Überraschung.

    Sämtliche GUI Frameworks die ich bisher gesehen habe machen das so (also mit optionalen Methoden). Natürlich heisst das nicht dass es deswegen die beste Lösung sein muss, aber es legt zumindest nahe dass die Lösung nicht total bescheuert ist 🙂



  • hustbaer schrieb:

    Sämtliche GUI Frameworks die ich bisher gesehen habe machen das so (also mit optionalen Methoden).

    Aber halt nur sehr begrenzt, nur weil es ein Tab-Ctrl-Item gibt, muss die Oberklasse nicht eine Funktion "AddTab" besitzen. Sollte nicht. Gibt es in GUI-Bibliotheken eigentlich noch mehr solcher optionaler Funktionen außer get-/set-Text? Mir fällt jedenfalls grad nichts ein.


Anmelden zum Antworten