Design so ok?



  • Hallo,

    ich bastel gerade an einem simplen GUI-API für mein Spielchen. Ziel soll es sein, dass sich mit diesem das Hauptmenü inklusive Untermenüs relativ leicht modellieren lässt.
    Ich poste mal meine Schnittstelle, damit ihr sie auseinander nehmen könnt. Das Ganze soll ausschließlich aus Benutzersicht (Benutzer == die Person, die diese Klassen nutzt) betrachtet werden.

    Man verzeihe mir bitte mein #pragma once, aber das compiliert einfach schneller.

    #pragma once
    
    #include "forwards.h"
    #include <vector>
    
    class Menu
    {
    public:
    	Menu();
    	~Menu();
    
    	// Rendert das Menü und alle registrierten Controls.
    	void draw() const;
    	// Berechnet Animationen im Menü.
    	void calc();
    	// Teilt dem Menü mit, dass der linke Mausbutton
    	// gedrückt wird.
    	void mouseDown();
    	// Teilt dem Menü mit, dass der linke Mausbutton wieder
    	// losgelassen wurde.
    	void mouseUp();
    
    private:
    	std::vector<Control*> mainMenuElements;
    	int mouseDownElement;
    	bool enabled, adjustingAlpha;
    	unsigned int alpha;
    
    	// Verboten!
    	Menu(const Menu&);
    	const Menu& operator=(const Menu&);
    };
    
    #pragma once
    
    #include "Point2D.h"
    #include <string>
    
    // Die Wurzelklasse aller Controls. Stellt eine
    // Schnittstelle zum rendern und für die grundlegende
    // Maus-Interaktion bereit.
    class Control
    {
    public:
    	// Rendert das Control.
    	virtual void draw(unsigned int alpha) = 0;
    	// Teilt dem Control mit, dass die Maustaste auf ihm
    	// gedrückt wurde.
    	virtual void mouseDown() = 0;
    	// Teilt dem Control mit, dass die Maustaste von ihm
    	// losgelassen wurde.
    	// Das muss nicht bedeuten, dass es angeklickt worden
    	// ist. Der Mauszeiger kann sich auch außerhalb des
    	// Controls befinden.
    	virtual void mouseUp() = 0;
    	// Teilt dem Control mit, dass es vollständig
    	// angeklickt wurde.
    	virtual void click() = 0;
    	// Frägt das Control, ob sich der Mauszeiger an der
    	// angegebenen Position über dem Control befindet.
    	virtual bool mouseOnControl(const Point<float>&);
    
    protected:
    	Control(	const Point<float>& location,
    				const Point<float>& size );
    
    	const Point<float> location;
    	const Point<float> size;
    
    private:
    	// Verboten!
    	Control(const Control&);
    	const Control& operator=(const Control&);
    };
    
    // Basisklasse für alle Button-ähnlichen Controls. Dazu
    // gehören z.B. Buttons selber und Checkboxen.
    class Button  :  public Control
    {
    public:
    	virtual void mouseDown();
    	virtual void mouseUp();
    
    protected:
    	Button(	const Point<float>& location,
    			const Point<float>& size );
    
    	inline bool isPressed() const	{return pressed;}
    
    private:
    	bool pressed;
    };
    
    // Der Buttontyp, der das Hauptmenü schmückt. Hat eine
    // feste Größe.
    class MenuButton  :  public Button
    {
    public:
    	MenuButton(	const Point<float>& location,
    				const std::wstring& caption,
    				void (*actionMethod)() );
    
    	virtual void draw(unsigned int alpha);
    	virtual void click();
    
    private:
    	const std::wstring caption;
    
    	void (*actionMethod)();
    };
    


  • Optimizer schrieb:

    void (*actionMethod)();

    macht mir bauchweh.
    der empfänger ist doch sicherlich ein objekt und keine globale funktion?

    wie macht man es in java?



  • In Java meldet man EventListener an, die von einer Listener-Klasse abgeleitet sind, wo man dann die entsprechende Methode redefiniert.
    Ich bin allerdings der festen Überzeugung, dass dies ein Workaround ist, da Java keine Funktionszeiger kennt.

    In .Net meldet man an den Listenern (dort hat jedes Control gleich seine verschiedenen Listener drin, also praktisch so wie bei mir, ich benachrichtige auch nicht noch extra einen Listener) Funktionszeiger an.

    // EventHandler ist von Delegate abgeleitet
    this.button1.Click += new System.EventHandler(/* Methodenzeiger */button1_Click);
    

    Der Empfänger ist der Button selber. Das Menü teilt ihm mit, dass er angeklickt wurde. Daraufhin führt er alles aus, was er für richtig hält (verändert seine Grafik) und ruft die Callback-Methode auf, die er vom Konstruktor bekommen hat, wenn sie nicht 0 ist.

    Mir würde noch einfallen, jeden Button einzeln abzuleiten und seine action-Methode jedesmal neu zu definieren oder Listener anzumelden, die dann selber wiederum Funktionszeiger haben oder wie in Java abgeleitet sind. Irgendwie kam mir das so jetzt praktischer vor.
    Was genau würdest du vorschlagen?



  • forum hat zum zweitenmal meinen beitrag gefressen. musst selber draufkommen oder mir ein forum zeigen, das funktioniert.



  • Dann kannst du mir jetzt ja helfen volkard :p 😉



  • Ich komme meinen Pflichten als Boost-Süchtling nach und bin für boost::function<void ()>. Rohe Funktionszeiger ohne UserData sind einfach zu unflexibel. Und selbst mit sind sie blöd zu verwenden. boost::function kann sowohl Funktionszeiger als auch jeden beliebigen Funktor mit der passenden Signatur annehmen (also z.B. das, was dabei herauskommt, wenn man mit boost::bind/boost::mem_fun eine Elementfunktion an eine bestimmte Instanz bindet). Der Overhead sollte kein Problem sein.

    boost::signal ist das ganze in größer, mit mehreren Receivern usw., aber mir hat boost::function immer gereicht. (Außerdem ist function include-only.)



  • Ok, dann schaue ich mir das mal an.
    Mich würde nur interessieren, ob volkard designtechnisch generell gegen Funktionszeiger war, oder gegen diesen Funktionszeiger?



  • operator void schrieb:

    eine Elementfunktion an eine bestimmte Instanz bindet

    gut.



  • Hier stand Unsinn.

    😑


Anmelden zum Antworten