Designfrage



  • ConfusedGuy schrieb:

    ich verweise hierbei auf das state pattern

    Wohl eher Strategy...



  • mh, ja. das trifft es wohl besser.
    aber sind die beiden da nich eh sehr ähnlich?

    hat das ding nun wieder eine methode die param2 auf true oder false setzt und betrachtest du param2 damit quasi als state passt auch das statepattern wieder. aber daran will ich dass nun nicht festmachen.(entsprechend geht das auch mit param1)

    aber der mechanismus ist der selbe, ob nun state pattern oder strategy pattern, wie von dir zu recht genannt.



  • Vielen Dank euch für die Antworten. An das Strategy-Pattern oder Policies (Template-basiert) habe ich auch schon gedacht. Hier sehe ich jedoch das Problem, dass die Funktionalität von z. B. methodD() nicht für sich abgeschlossen herausgenommen werden kann. Alle Funktionen greifen auf eine Vielzahl der privaten Membervariablen zu, so dass also dann den einzelnen Strategies/Policies noch ein Instanzzeiger übergeben werden müsste oder zumindest dürften die Strategies keine eigene Klasse sein, sondern nur verschiedene Memberfunktionen.

    Vielleicht ist der Weg über das Ableiten von einer gemeinsamen Basisklasse doch der beste... Hierbei steigt dann natürlich die Anzahl der abgeleiteten Klassen exponentiell mit der Parameterzahl des Algorithmus. Ich bin noch unentschlossen.

    Grüße



  • Hallo

    Vielleicht kann man hier auch eine Generische Programmierung benutzen.

    So zum Beispiel:

    template< bool t, int t1, int t2 >
    struct Select
    {
        enum { num = t1 };
    };
    
    template< int t1, int t2 >
    struct Select <true, t1, t2>
    {
        enum { num = t2 };
    };
    
    template< int t1, typename t2 >
    struct cTest
    {
        enum FuncType
        {
            Func1,
            Func2
        };
    
        typedef typename Select< (t1 > 42), Func1, Func2  >::type   tFuncType;
    
        void foo()
        {
            void (cTest::*fptr[])() =
            {
                &cTest::func1,
                &cTest::func2
            };
    
            *fptr[tFuncType::num]();
        }
    
        void func1();
        void func2();
    
    };
    

    Der Compiler Optimiert dann alles überflüssige raus.

    Lichtlein


  • Mod

    Bloops schrieb:

    Vielleicht ist der Weg über das Ableiten von einer gemeinsamen Basisklasse doch der beste... Hierbei steigt dann natürlich die Anzahl der abgeleiteten Klassen exponentiell mit der Parameterzahl des Algorithmus. Ich bin noch unentschlossen.

    Meine Güte, was hast du denn vor? Wieviele unterschiedliche Anpassungen eines Algorithmus kann es denn geben?

    Bevor du jetzt zu tief in das Thema einsteigst, vergleich doch einmal die Methode mit der if-Abfrage und eine handoptimierte Version. Wenn bei der Abfrage nämlich immer das gleiche passiert, kann die sehr gut vom Prozessor optimiert werden. Und dann lohnt es sich eventuell überhaupt nicht, wenn du jetzt tagelang darüber nachdenkst wie du diese eine Abfrage einsparst.



  • Template-Spezialisierungen:
    Was nett wäre, wäre, dass die relevanten Funktionen einfach spezialisiert würden, aber nur einzelne Funktionen und nicht die ganze Klasse zu spezialisieren ist meines Wissens ja nicht möglich...

    Du kannst die Methoden auch überladen.

    z.B. so:

    #include <iostream>
    using namespace std;
    template <int T>
    struct int2type
    {
    	enum {e = T};
    };
    template <int T1, int T2>
    class Algorithmus
    {
    	void method (int2type<1>,int2type<0>) const { cout << "parma1 größer 42, param2 false\n";}
    	void method (int2type<1>,int2type<1>) const { cout << "parma1 größer 42, param2 true\n";}
    	void method (int2type<0>,int2type<0>) const { cout << "parma1 kleiner/gleich 42, param2 false\n";}
    	void method (int2type<0>,int2type<1>) const { cout << "parma1 kleiner/gleich 42, param2 true\n";}
    public:
    	void run_method() const { method(int2type<T1>(),int2type<T2>());}
    };
    int main()
    {
    	int const param1 = 42;
    	bool const param2 = true;
    	Algorithmus<(param1>42?1:0),param2> al;
    	al.run_method();
    }
    


  • Bloops schrieb:

    Da das ganze Programm zumindest weiche Echtzeit "garantieren" sollte, ist die Implementierung also aufgrund der häufigen Aufrufe von methodC() und methodD() absolut relevant und es handelt sich hierbei nicht um premature optimization. Also bitte nicht meckern 😉

    So lange du nicht den Profiler drüberlaufen hast lassen ist es trotzdem premature. Warum? Weil du keine Ahnung hast wo die Zeit draufgeht. Blind rumoptimieren macht keinen Sinn. Und Optimieren, wenn etwas bereits schnell genug ist, macht auch keinen Sinn. Auch wenn "soft realtime" gefragt ist und Schleifen vorhanden sind die 10M mal durchlaufen werden. Schnell genug ist schnell genug, daran ändert auch die Anforderung "soft realtime" nix.



  • hustbaer schrieb:

    So lange du nicht den Profiler drüberlaufen hast lassen ist es trotzdem premature. Warum? Weil du keine Ahnung hast wo die Zeit draufgeht.

    Und woher weißt du das? 😉 Ich habe sehr wohl einen Profiler drüberlaufen lassen und weiß genau, an welchen Stellen sich die Flaschenhälse befinden.

    @einwurf: Auf die Idee kam ich noch gar nicht. Der Ansatz ist mir jetzt auf den ersten Blick relativ sympatisch.

    SeppJ schrieb:

    Meine Güte, was hast du denn vor? Wieviele unterschiedliche Anpassungen eines Algorithmus kann es denn geben?

    Vermutlich werden es drei sein, was in meinem Fall dann 2^3 = 8 Kombinationen wären, wobei nicht alle Parameter für alle Funktionen relevant sind. Mal hängt eine Funktion nur von einem ab, mal von allen dreien und mal von gar keinem.

    @Lichtlein: Huh... Eigentlich bin ich auf der Suche nach einer Implementierung, die das Ganze übersichtlicher gestaltet...

    Daher würde ich am liebsten auch die ganzen if-Abfragen innerhalb der Funktionen verhindern, selbst wenn sie vom Prozessor recht effizient ausgewertet werden können.

    Vielen Dank übrigens allgemein euch allen für die vielen Anregungen. Sind viele interessante Denkanstöße dabei.

    Grüße



  • hustbaer schrieb:

    Bloops schrieb:

    Da das ganze Programm zumindest weiche Echtzeit "garantieren" sollte, ist die Implementierung also aufgrund der häufigen Aufrufe von methodC() und methodD() absolut relevant und es handelt sich hierbei nicht um premature optimization. Also bitte nicht meckern 😉

    So lange du nicht den Profiler drüberlaufen hast lassen ist es trotzdem premature. Warum? Weil du keine Ahnung hast wo die Zeit draufgeht. Blind rumoptimieren macht keinen Sinn.

    Die Entwürfe sind offensichtlich unbefriedigend. Um das zu sehen, brauche ich keinen Profiler.



  • Bloops schrieb:

    Problem hierbei: die if-Abfrage wird 10.000.000 Millionen mal ausgeführt, obwohl bei Konstruktion des Algorithmus -Objekts (eigentlich sogar zur Compilezeit) bereits feststeht, welcher Zweig der aktive ist.

    Also wenn es tatsächlich zur Compile-Zeit feststeht kann man noch besser in die Trick-Kiste greifen.

    Ich würde den Algorithmus selbst als Template-Policy umsetzen.
    Die Extra-Work als Sonderfall könnte man als Template-Decorator realisieren.

    Quasi sowas wie

    typedef Algorithm < NormalCalculation > standard;
    typedef Algorithm < ExtraCalculation < NormalCalculation > > mitExtra;
    // Standard mit anderer Schreibweise
    typedef Algorithm < NoExtraCalculation < NormalCalculation > > standardWithNoop;
    

    Zuerst wird vom innersten Template compute aufgerufen und dann vom darüberliegenden.


Anmelden zum Antworten