Design-Problem: Composite ohne friend



  • @ Dravere:
    Ja, schon so. Dass es grundsätzlich noch möglich ist, ist mir bewusst (gerade gestern habe ich dazu was geschrieben). Aber man kann nur noch über die Indirektion Component:: die Basisklassenversion aufrufen, was immerhin schon einige Fehler abfangen dürfte. Die Funktion dennoch aufzurufen würde ich fast schon als böswillig einstufen.

    Oder meinst du, ich sollte auf das using verzichten?



  • @ camper:
    Hm. Ich versuche friend eigentlich zu umgehen, allerdings nehme ich dafür zwei zusätzliche Indirektionsfunktionen (momentan für Events und Zeichnen, möglicherweise kommen noch mehr dazu) und die Gefahr, von abgeleiteten Klassen immer noch Zugriff zu haben, in Kauf (wobei letzteres wie angetönt vertretbar sein sollte).

    Ich verwende eben schon sonst ab und zu friend und möchte nicht dazu tendieren, wegen der verlockenden Einfachheit unüberlegte Entscheidungen zu treffen. Wenn allerdings die Alternative mehr Aufwand und mehr Nachteile mit sich bringt, ist der Verzicht vielleicht schon etwas fragwürdig...


  • Administrator

    Nexus schrieb:

    Oder meinst du, ich sollte auf das using verzichten?

    *schulterzuck*
    Ich glaube darüber könnte man sich endlos streiten, ob das sinnvoll ist oder nicht. Somit bin ich der Meinung, dass das eine sehr subjektive Sache ist. Ich würde vielleicht den Namen ein wenig anders wählen: forwardEventTo(Event const&, Component& cmp). Man könnte sogar noch ein static davor setzen oder sowas. Ich würde probieren den Schutz davor, dass man diese Funktion nicht absichtlich aufruft, eher in Component einbauen, damit gleich jeder, welcher von Component ableitet, von diesem Schutz profitiert. Allerdings ist schon fragwürdig, ob es überhaupt so einen Schutz braucht?

    Zur Lösung von camper:
    Es sind zwei ganz unterschiedliche Ziele, welche da erreicht werden, die musst du unterscheiden Nexus. Die Lösung von camper erlaubt wirklich nur der Klasse Panel den Zugriff. Wenn nun ein Programmierer kommt, welcher deine Bibliothek verwendet, und eine eigene spezielle Version einer Panelklasse erstellen möchte, dann hat er keine Möglichkeit dies zu machen. Du würdest ihn somit völlig in seinen Möglichkeiten behindern. Vielleicht ist es gewollt, dann ist die Lösung von camper die bessere, ansonsten würde ich eher die andere nehmen 😉

    Mit friend kann man wirklich geschlossene Funktionalität erreichen, welche niemand von aussen für eigene Zwecke nutzen kann.

    Grüssli



  • Designhinweis.

    Du solltest zwischen dem Logik (Beispielweise das ein Button klickbar ist) und wie der Benutzer dieses Implementiert unterscheiden.

    class Button
    {
    public:
      void (*Clicked)(const Event& e);
    };
    
    class MyButton : public Button
    {
    public: 
      MyButton() : Clicked(&ClickHandle) {
      }
    
    private:
      void ClickHandle(const Event& e) {
      ...
      }
    };
    


  • Dravere schrieb:

    Ich würde probieren den Schutz davor, dass man diese Funktion nicht absichtlich aufruft, eher in Component einbauen, damit gleich jeder, welcher von Component ableitet, von diesem Schutz profitiert. Allerdings ist schon fragwürdig, ob es überhaupt so einen Schutz braucht?

    Du meinst private ? Oder doch eher throw UnsupportedOperationException ? 😃

    Nein, du hast schon Recht. Aber Component ist eigentlich nicht dafür vorgesehen, vom Benutzer direkt abgeleitet zu werden (eine GUI-Komponente ist bei mir entweder elementar oder eine Sammlung anderer Komponenten). Ich muss aber sagen, dass mein Framework noch nicht sehr weit entwickelt ist, möglicherweise werde ich hier noch einiges ändern. Ein detail -Namensraum wäre vielleicht ein Weg, um zu zeigen, dass die Klasse für die Implementierung reserviert ist.

    Dravere schrieb:

    Es sind zwei ganz unterschiedliche Ziele, welche da erreicht werden, die musst du unterscheiden Nexus. Die Lösung von camper erlaubt wirklich nur der Klasse Panel den Zugriff. Wenn nun ein Programmierer kommt, welcher deine Bibliothek verwendet, und eine eigene spezielle Version einer Panelklasse erstellen möchte, dann hat er keine Möglichkeit dies zu machen.

    In der HandleEvent() -Funktion wird zuerst HandleEvent() für alle Subkomponenten aufgerufen, und dann OnEvent() für die aktuelle Instanz, welche vom Benutzer implementiert werden kann. Das Verhalten möchte ich nicht ändern, mittels OnEvent() sind ja benutzerdefinierte Event-Reaktionen in Panel -Derivaten immer noch möglich.

    Zeus schrieb:

    Du solltest zwischen dem Logik (Beispielweise das ein Button klickbar ist) und wie der Benutzer dieses Implementiert unterscheiden.

    Ja, aber ich will den Benutzer eines Button s nicht zur Vererbung zwingen. Ich verwende hier das Observer-Pattern und biete die Möglichkeit, Callbacks zu registrieren. Dann kann auch mehr als nur eine Aktion mit dem Mausklick assoziiert werden.



  • Vererbung stört? Na dann ohne:

    void ClickHandle(const Event& e)
    {
    }
    
    int main()
    {
      Button button();
      button.Clicked = &ClickHandle;
      button.Clicked() // Manuell Fire Click.
      return 0;
    }
    

    Nun die Vererbung ist nicht das stört. Was du brauchst ist ein öffentlich Schnistelle um Clicken auszulösen.



  • Zeus schrieb:

    Nun die Vererbung ist nicht das stört. Was du brauchst ist ein öffentlich Schnistelle um Clicken auszulösen.

    Wie gesagt habe ich dafür schon einen flexibleren Ansatz gefunden.

    void ClickAction()
    {
        std::cout << "Klick." << std::endl;
    }
    
    int main()
    {
        Button Btn;
        Btn.AddMouseClickListener(&ClickAction);
        Btn.Click();
        // ...
    }
    

    Öffentliche Member sind sicher keine Option.



  • Nexus schrieb:

    Zeus schrieb:

    Nun die Vererbung ist nicht das stört. Was du brauchst ist ein öffentlich Schnistelle um Clicken auszulösen.

    Wie gesagt habe ich dafür schon einen flexibleren Ansatz gefunden.

    void ClickAction()
    {
        std::cout << "Klick." << std::endl;
    }
    
    int main()
    {
        Button Btn;
        Btn.AddMouseClickListener(&ClickAction);
        Btn.Click();  // <- öffentlich Schnistelle, da ist das Teil wovon ich spreche
        // ...
    }
    


  • In deinem ersten Post hast du zwar nichts davon gesagt, aber okay. 😉

    Dass ich Mausklicks mittels Click() simulieren kann, ist ja schön und gut. Ob eine Schaltfläche diese Funktionalität unbedingt braucht, ist wieder eine andere Frage. Ich habe sie zumindest.



  • Qt, WCF haben Sie auch *gg* blos Swing nicht, da ist die Methode protected 😛



  • Zeus schrieb:

    blos Swing nicht, da ist die Methode protected 😛

    Nein, auch da ist sie public :
    http://java.sun.com/javase/6/docs/api/ und dann bei JButton nach doClick() suchen.

    Welcher Name gefällt euch eigentlich besser, "DoClick" oder "Click"? Mich stört das nichtssagende "Do" ein wenig.


  • Administrator

    Zeus schrieb:

    Qt, WCF haben Sie auch *gg* blos Swing nicht, da ist die Methode protected 😛

    Hmmm, gute Argumentation 🤡 :p

    Ich habe so eine Methode noch nie benötigt und hab sie bis anhin nur missbraucht gesehen. Statt dass man die Funktionalität in eine externe Funktion ausgelagert hat, welche im Click-Event aufgerufen wird und auch von extern aufgerufen werden kann, haben es die Leute in die Handlerfunktion geschrieben und dann per DoClick oder dergleichen die Handlerfunktion aufgerufen. Wodurch meistens noch ein unnötiges Event erzeugt werden musste.

    Ich sehe ehrlich gesagt keinen sinnvollen Verwendungszweck für so eine Funktion.

    Grüssli



  • Dravere schrieb:

    Ich sehe ehrlich gesagt keinen sinnvollen Verwendungszweck für so eine Funktion.

    Da ich intern mit std::tr1::function arbeite, wird der Anwender auch ab und zu std::tr1::bind() benutzen, um mal schnell lokale Funktoren zu übergeben. Dabei kann ich mir vorstellen, dass es angenehmer ist, später einfach Click() aufzurufen, statt zu rekonstruieren, welche Funktionen alle gespeichert wurden, diese neu mit bind() zusammenzubauen und jeweils manuell aufzurufen. Und Funktionen jeweils neu zu definieren macht bind() auch hinfällig. Denn die Sache sieht im Allgemeinen ja nicht so trivial wie im oberen Codebeispiel aus, wo man ClickAction() gerade so gut direkt aufrufen könnte. Im Extremfall wäre es notwendig, über die gespeicherten Funktionen Buch zu führen (d.h. sie doppelt zu speichern) oder auf der User-Seite einen Callback-Mechanismus einzurichten.

    Gesamthaft gesehen denke ich, man kann sich durch eine Click() -Funktion so einiges an Komplexität sparen. Natürlich kann sie auch missbraucht werden, aber das halte ich jetzt nicht gerade für das K.O.-Kriterium.


  • Administrator

    @Nexus,
    Nur ist nicht bereits eine solche Verwendung eine falsche Verwendung? Wenn jemand die Anwendungslogik mit einem Button so verbindet, dass er die Übersicht verliert, erscheint es mir nicht gerade eine sinnvolle Lösung 😉
    Wie oft wird zudem mehrere Anwendungslogik über mehrere Funktoren mit einem Button-Click verbunden? Erscheint mir alles ein wenig sehr theoretisch hinkonstruiert, wodurch deine Schnittstelle fett wird und leicht zu falscher Verwendung führen kann.

    Ich frage mich halt, wieso baust du so ein Feature ein? Dass ein Programmierer theoretisch dies verwenden kann und du ein zusätzliches Feature auf die Liste packen kannst? Oder siehst du wirklich eine praktische sinnvolle Anwendung, also ein Beispiel aus der Praxis oder sowas? Ein Feature einbauen, nur weil man kann, halte ich nicht für sehr klug.

    Grüssli



  • Ich kann mir durchaus Verwendungsgebiete von einer solchen Funktion vorstellen.

    http://tinyurl.com/yfgow2f

    Da ist dann das einfügen in ein Textfeld auch gut, wenn man das nicht so lösen muss, dass man Tastatureingaben simuliert.


  • Administrator

    Jetzt müsstest du nur noch einen gültigen Link hinschreiben, damit ich mir das auch anschauen kann 😃

    Grüssli



  • Dravere schrieb:

    Nur ist nicht bereits eine solche Verwendung eine falsche Verwendung? Wenn jemand die Anwendungslogik mit einem Button so verbindet, dass er die Übersicht verliert, erscheint es mir nicht gerade eine sinnvolle Lösung 😉

    Was hat das mit Übersicht verlieren zu tun? Das ist ja der Sinn eines Callback-Systems. Man registriert die Funktionen einmal und fertig. Die Reaktionen auf Events können logisch und lokal getrennt von der Registrierung erfolgen, was Programmbestandteile entkoppelt.

    Dravere schrieb:

    Wie oft wird zudem mehrere Anwendungslogik über mehrere Funktoren mit einem Button-Click verbunden? Erscheint mir alles ein wenig sehr theoretisch hinkonstruiert, wodurch deine Schnittstelle fett wird und leicht zu falscher Verwendung führen kann.

    Warum soll es nicht möglich sein, mehrere Aktionen mit einem Klick zu verbinden? Kann zum Beispiel sehr gut sein, dass man durch den Klick gerade andere GUI-Komponenten aktiviert oder deaktiviert (kennst du sicher, dass gewisse Häkchen in CheckBoxes grau und nicht anklickbar werden, wenn andere aktiv sind). Wenn man nur eine Reaktion zulässt, muss der Benutzer wieder auf seiner Seite Funktionen schreiben, die alles tun. Sind solche Massenfunktionen etwa besser, als wenn die Funktionalität direkt aufgeteilt wird und die einzelnen Komponenten logisch getrennt reagieren können?

    Ich verstehe nicht, wieso der Benutzer einen Button drücken darf und der Programmierer nicht.

    Dravere schrieb:

    Ein Feature einbauen, nur weil man kann, halte ich nicht für sehr klug.

    Stell dir vor, das ist mir sogar bewusst! 😉

    Aber du darfst grundsätzlich davon ausgehen, dass ich mir beim Design schon etwas überlege. Vielleicht übersehe ich ab und zu etwas oder gestalte etwas anders als z.B. du es tun würdest, aber leichtsinnig Features hinzufügen, nur um möglichst viele davon zu haben, ist eigentlich nicht mein Beweggrund.

    drakon schrieb:

    http://@notallowed@.com/yfgow2f

    Dein Link scheint irgendwie zensiert... Ist das nur bei mir so?


  • Administrator

    Hmmm, habe gerade meine ganze Antwort gelöscht. Ich glaube, dass dies am Ende auf zu viele subjektive Vorstellungen hinausläuft. Vieles würde ich auch gänzlich anders machen. Ich finde es einfach ein wenig unsinnig, wenn ein Programmierer eine GUI Bibliothek gleich verwenden muss, wie die Anwender, welche das GUI bedienen werden. Sowas führt zu keinem guten Aufbau, aber das ist wohl eben eine sehr subjektive Meinung 😉

    Grüssli



  • Okay. Dennoch danke für die vielen Anregungen in diesem Thread! Der Programmierer bedient die GUI ja nicht gleich wie der Anwender, das ist jetzt nur ein Spezialfall. Aber mit dem Subjektiven hast du wahrscheinlich Recht, dir würde mein Framework wohl nicht besonders gefallen. 😉

    Über das ursprüngliche Problem habe ich nochmals nachgedacht, und ich denke, ich werde es vorerst doch mit friend machen, um den Zugriff auf die zwei Klassen zu beschränken. Aber das kann sich auch wieder ändern. Auf jeden Fall werde ich mir die Indirektion über Forward-Funktionen für protected -Zugriff merken!



  • Ehm.. wtf? - Das war ein Link auf "Let me google that for you", wo ja ein Mausklick in dem Sinne gemacht wird, ohne dass es die Maus macht..
    Wahrscheinlich hat das Markus mal zensiert.. 😉


Anmelden zum Antworten