Template Method Pattern anstatt if-else?
-
Finix,
du sagst, das TMP habe nichts mit Templates zu tun und gehst dann dazu über, mit virtuellen Funktionen um Dich zu schmeißen. Da muss ich Einspruch erheben, denn das TMP lässt sich natürlich vorzüglich mit Templates umsetzen – und in C++ würde ich im Zweifelsfall immer diese Methode verwenden, statt virtueller Funktionen.
Dass diese Methode in einem Thread im C++-Forum nicht einmal erwähnt wird, finde ich schade.
-
Also mich würde brennend interessieren wie das über Templates realisierbar wäre Konrad. Könntest Du vielleicht den vorig geposteten Beispielcode (so schlimm er auch ist) evtl. über Templates ins TMP packen?
Und was ist der Grund warum über Templates das TMP "besser" ist als über virtuelle Funktionen ?
Praktisch also im cpp-File. Das ist auch von den Abhängigkeiten her sinnvoller.
wenn ich gleichzeitig mit Templates arbeite - ist das dan auch möglich?
Weil sowas in ne cpp-zu schreiben geht ja net:template <class T> Class_Base<T>* Switch_Algorithm<T>::Get_Algorithm(const int opt_level) { switch(opt_level) { case no_opt: return new C1<T>(); case opt_1: return new C2<T>(); } }Könnte es sein, dass ich hier nicht drumrumkomme die Get_Algorithm in ne eigene Klasse bzw. in eine andere Klasse zu packen?
Danke
-
das würde ich auch gerne sehen. ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll. c++ templates werden doch zur compilezeit festgelegt, wie können die virtuelle funktionen ersetzen?
-
Zur obigen Frage mittels der vermeideung von virtuellen Funktionen habe ich folgenden Thread gefunden:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-192539-and-view-is-next.htmlNur was da der vorteil sein soll weiß ich (noch) nicht. Evtl. sind die Kosten um virtuelle Funktionen aufzurufen etwas teurer bezüglich Zeit?
Ich hätte da noch eine Frage und
ich weiß jetzt nicht ob ich deswegen einen neuen Thread aufmachen soll, deswegen poste ich lieber hier weil es könnte sein dass es eher hier her gehört:ich würde gerne folgenden Code so "vereinfachen", dass das switch statement entfällt. Nun bin ich mir nicht sicher ob es überhaupt geht - evtl. irgendwie über virtuelle Funktionen oder so?
Matrix<double> *A_REAL = NULL; Matrix<COMPLEX> *A_COMPLEX = NULL; switch(o_rm.Get_Type(file)) { case real: { std::cout << "\t Input is REAL!" << std::endl; o_rm.Read_Matrix(file, &A_REAL); // Mache was mit A_REAL; delete A_REAL; break; } case complex: { std::cout << "\tInput is COMPLEX" << std::endl; o_rm.Read_Matrix(file, &A_COMPLEX); // mache was mit A_COMPLEX delete A_COMPLEX; break; }In Read_Matrix gibt es Template-Spezifikationen, die unterschiedlich vom Template, die Matrix unterschiedlich aufbauen. Braucht ihr noch mehr Informationen?
Ich kann also erst zur Laufzeit entscheiden welches Template benutzt wird...
Das ganze läuft in meiner main(), also in keiner Template Klasse
-
ja schrieb:
ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll.
Das lese ich nirgendwo. Wenn das der Fall ist, sind Templates in der Tat nich geeignet.
Mati schrieb:
Und was ist der Grund warum über Templates das TMP "besser" ist als über virtuelle Funktionen ?
Erstmal, weil es der C++-ige Weg wäre (so arbeitet ja auch die STL, wo möglich). Außerdem ist die Methode wesentlich effizienter als die über virtuelle Funktionen, weil die Auflösung eben zur Compilezeit und nicht zur Laufzeit geschieht.
Der Code sähe prinzipiell genauso aus, nur dass die Funktionen nicht virtuell gemacht werden und die Ziel-Klasse parametrisiert wird.
struct Algorithm1 { void Method1() {} void Method2() {} } struct Alg2 { void Method1() {} void Method2() {} } void my_method<T>(T const& algorithm) { algorithm.Method1(); // … was anderes … algorithm.Method2(); } // Aufruf: if (flag) my_method(Algorithm1()); else my_method(Alg2());
-
hmm...wie viel Zeit kostet denn der aufruf virtueller funktionen im vergleich?
Ist das nur marginal oder doch "zu spüren".
Bei mir würde es nur einmal passieren.Ich schaffe es leider über Templates nicht so ganz:
template <class T> class Algorithm1 { public: void Method1() {} void Method2() {} }; template <class T> class Alg2 { public: void Method1() {} void Method2() {} }; template<class T> T* Algorithm::Get_Algorithm(const int opt_level) { switch(opt_level) { case 0: return new Algorithm<T>(); case 1: return new Alg2<T>(); } } Base<T>::my_method(T* algorithm) { algorithm->Method1(); // … was anderes … algorithm->Method2(); } // in der main: Algorithm<double> o_alg; Base<double> o_base; o_base.my_method(o_alg.Get_Algorithm(0));mein error:
Fehler: »C<double>*« kann nicht nach »double*« in return umgewandelt werden main.cpp:154: instantiated from hereSchon klar der feher aber wie kann ich denn den switch in eine eigene Klasse packen um von dort den richtigen klassentyp zu returnen?
-
Konrad Rudolph schrieb:
Finix,
du sagst, das TMP habe nichts mit Templates zu tun und gehst dann dazu über, mit virtuellen Funktionen um Dich zu schmeißen. Da muss ich Einspruch erheben, denn das TMP lässt sich natürlich vorzüglich mit Templates umsetzen – und in C++ würde ich im Zweifelsfall immer diese Methode verwenden, statt virtueller Funktionen.
Dass diese Methode in einem Thread im C++-Forum nicht einmal erwähnt wird, finde ich schade.
Hier nochmal zum Mitschreiben: das Template Method Pattern hat nichts mit Templates zu tun! Es geht darum dass TMP != Templates und vor allem auch TMP != Template Technik.
Und in C++ stehst du nun mal oft nicht vor dem "Zweifelsfall" sondern vor Limitierungen der Sprache - sollte ich mich täuschen, wäre ich dir sehr verbunden würdest du mir deine Variante, rein mit Templates, in diesem Fall ("über ein input-flag an und ausschaltbar"), vorstellen.
-
Konrad Rudolph schrieb:
ja schrieb:
ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll.
Das lese ich nirgendwo. Wenn das der Fall ist, sind Templates in der Tat nich geeignet.
ich hätte es so verstanden, weil er sich über die vielen if-else beschwert hat.
@Mati
Für dein switch Problem könnte das State Pattern was sein.
-
Danke ja - aber um ehrlich zu sein bin ich mir (egal wieviel ich übers state pattern lese) nicht sicher obs gehen wird...ich brauch auch immer einen beispielcode...ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.
-
Mati schrieb:
ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.
Du kennst google?
Du weißt wie man Suchworte in google einsetzt?Such mal nach: State Pattern C++
Und wenn du da wirklich nichts findest (bezweifel ich wenn ich das Überblicke), dann kannst du gerne weiter fragen... (Hinweis: selbst mit state muster c++ und Einschränkung auf deutsche Seiten wird man fündig...)
cu André
-
Mati schrieb:
ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.
Wie wäre es hiermit: http://en.wikipedia.org/wiki/State_pattern
-
Hallo?
Ich sagte bereits:sinnvolle einfache C++ bespiele zum state pattern finde ich nicht
unter google finde ich nur umfangreiche und nicht gerade erklärende beispiele.
Aber gut - damit muss ich wohl leben.
-
Mati schrieb:
Praktisch also im cpp-File. Das ist auch von den Abhängigkeiten her sinnvoller.
wenn ich gleichzeitig mit Templates arbeite - ist das dan auch möglich?
Nein, dann natürlich nicht (außer mit export etc. blahblah). Das entscheidende ist, dass der Ctor außerhalb der Klasse base definiert sein muss.
class Base { public: Base(); }; // ich schreib hier die Klassendefinition direkt hin, bei dir wäre ein // #include "derived.h" an dieser Stelle class Derived : public Base { public: Derived() { blub(); } }; // jetzt ist Derived erst bekannt und kann konstruiert werden Base::Base() { new Derived() ... }
-
Ok, dann versuche ich eine konkrete Frage zu stellen:
Ich bin mir nicht sicher ob ich missverstanden wurde. Ich will in der main() einen Template - Typ zurückbekommen, der anhand eines int-werts entweder double oder COMPLEX ist.
Ich kann ja aus der main keine Templates initialisieren. Also sowas geht ja in der main nicht:
Abstract_Matrix ab_mat; // 0 für real // 1 für complex Matrix<T>* matrix = ab_mat.Get_Type(0); //in der Abstract_Matrix template <class T> class Abstract_Matrix { public: T* Get_Type(int c) { if (c) return new Matrix<double>; else return new Matrix<COMPLEX>; } };so ist es ja alles schwachsinn.
Nach diesem Satz:
This is a clean way for an object to partially change its type at runtime.Unter diesem Link:
http://en.wikipedia.org/wiki/State_patterngibt es die folgenden Methoden:
selectPenTool() { currentTool.reset(new PenTool); } selectSelectionTool() { currentTool.reset(new SelectionTool); }Allerdings ist mir schleierhaft wie ich denn einen Template-Basierten DrawingController bauen sollte, der seinen Template-Typ von double nach COMPLEX ändert bzw. wechseln braucht er ja nicht mal. Er braucht ihn ja nur zu initialisieren....
-
Wie schon mehrfach angedeutet, mit einem rein Template-basiertem Ansatz kannst du da zur Laufzeit nicht viel machen.
Davon abgesehen hast du dich mit deinem Matrix-Beispiel, relativ gesehen, kilometerweit von deinem Ausgangsproblem entfernt.
So kann dir nicht nur keiner wirklich helfen, irgendwann fühlen sich die Leute auch ein wenig verarscht. Wenn du Verständnisprobleme mit einem der Pattern hast und diesbezüglich Erleuchtung suchst, mach das deutlich und öffne einen neuen Thread dazu. Wenn du Hilfe zu deinem konkreten Problem suchst, dann leg endlich mal dar was du überhaupt machen willst.
-
Wie schon mehrfach angedeutet, mit einem rein Template-basiertem Ansatz kannst du da zur Laufzeit nicht viel machen.
So etwas wollte ich wissen - mehr nicht.
Davon abgesehen hast du dich mit deinem Matrix-Beispiel, relativ gesehen, kilometerweit von deinem Ausgangsproblem entfernt.
So kann dir nicht nur keiner wirklich helfen, irgendwann fühlen sich die Leute auch ein wenig verarscht.Ich habe geschrieben, dass ich ein weiteres Problem habe, wo ich nicht sicher bin ob es evtl. über Patterns lösbar wäre, evtl. sogar über das TMP. Deshalb hier gepostet.
Wenn man neue Threads aufmacht wird man angeschnauzt man öffne für dieselben Probleme ständig neue Threads.
Wenn man in gleichen Threads arbeitet genauso - also was nun?Wenn du Verständnisprobleme mit einem der Pattern hast und diesbezüglich Erleuchtung suchst, mach das deutlich und öffne einen neuen Thread dazu.
Mit so einem Threas käme als Antwort: Schau bei google oder benutze die Suchfunktion. Auf solche Antworten kann ich verzichten - so ein Thread bringt überhaupt nichts.
Wenn du Hilfe zu deinem konkreten Problem suchst, dann leg endlich mal dar was du überhaupt machen willst.
Mein konkretes problem habe ich dargestellt. Das kann mir nicht angekreidet werden. Vorgeworden werden kann mir nur, dass ich mich sehr unverständlich ausgedrückt habe.
-
Mati schrieb:
Ich habe geschrieben, dass ich ein weiteres Problem habe, wo ich nicht sicher bin ob es evtl. über Patterns lösbar wäre, evtl. sogar über das TMP. Deshalb hier gepostet.
Wenn man neue Threads aufmacht wird man angeschnauzt man öffne für dieselben Probleme ständig neue Threads.
Wenn man in gleichen Threads arbeitet genauso - also was nun?Tatsächlich. Hatte's zuvor nur überflogen - sah so aus als ginge es um das selbe Problem, insbesondere weil's danach wieder nahtlos zum Ursprungsproblem überging.
Ist halt schon praktischer für verschiedene Fragen verschiedene Threads zu öffnen. Da wird auch sicher niemand meckern.
Mati schrieb:
Wenn du Verständnisprobleme mit einem der Pattern hast und diesbezüglich Erleuchtung suchst, mach das deutlich und öffne einen neuen Thread dazu.
Mit so einem Threas käme als Antwort: Schau bei google oder benutze die Suchfunktion. Auf solche Antworten kann ich verzichten - so ein Thread bringt überhaupt nichts.
Definiere "so ein Thread". Wenn du nur mit einem "das XY funzt bei mir nich?!!1" ankommst, dann klar, so was wird zerrissen. Ein "Ich habe mir unter [link] das XY-Pattern angeschaut und verstehe [irgendeinen Punkt] nicht" o.ä. wird da ganz anders aufgenommen.
Mati schrieb:
Mein konkretes problem habe ich dargestellt. Das kann mir nicht angekreidet werden. Vorgeworden werden kann mir nur, dass ich mich sehr unverständlich ausgedrückt habe.
Siehe oben. Ich dachte die Matrix-Geschichte gehört irgendwie dazu, ist das eigentliche Problem oder gar nur ein anderes, potentiell unverwandtes Beispiel.

-
Meine Konkrete Frage zum Template Method Pattern ist:
Wenn eine abgeleitete Klasse nur zusätzlich etwas tun soll, wie bringt man das in das "Skelett" ein?
Es macht doch wenig Sinn eine dummy-variable anzulegen die im Fall A nicht intialisiert wird und im Fall B schon, diese dann an die virtuelle Fuktion zu übergeben oder?
Aber andererseits müsste man doch wieder das ganze Skelett virtuell machen, wenn man in einer Version zusätzliche Variablen verwenden will und in der anderen nicht?Ich versuche meine Frage an einem pseudo-beispielcode zu erklären:
class skelett { public: void my_algorithm() { int* a = NULL; init_b(); // hier die funktion: a = make_this(); compute(a); } void init_b(//do something) {...}; protected: virtual void compute(int* a) = 0; virtual int* make_this() = 0; }; class FallA : public skelett { protected: virtual int* make_this() { return NULL; } virtual void compute(int* a) { //mache was ohne a zu verwenden } }; class FallB : public skelett { protected: virtual int* make_this() { //initialisiere a a = new int[4]; } virtual void compute(int* a) { //hier mit a irgendwelche sachen machen } };
-
Ja ja. Ich würde jetzt gern was über metasyntaktische Symbole und konstruierte Grenzfälle vom Stapel lassen, aber mir fällt nichts ein.
Naja. Wie dem auch sein. Mach einfach ein konkretes Beispiel daraus, und ich sage dir, aus meiner Sicht, warum das so OK ist/wie man das richtig bzw. besser umsetzt/warum das Pattern ungeeignet ist.
-
Puh....das ist gar nicht so einfach ein konkretes Beispiel zu machen....
//in der main wird der algorithmus also das skelett aufgerufen: Switch_Algorithm<COMPLEX> o_alg; Spai<COMPLEX>* alg_ptr = o_alg.Get_Algorithm(opt_level); alg_ptr->SPAI_Algorithm(...);// hier ist meine switch-methode - was besseres ist mir nicht eingefallen als sie in ne eigene Klasse zu packen... template <class T> Spai<T>* Switch_Algorithm<T>::Get_Algorithm(const int opt_level) { std::cout << "\t Optimizations for SPAI:\n"; switch(opt_level) { case no_opt: return new Spai_Standard<T>(); case caching: return new Spai_Caching<T>(); // no default case } }Hier das Skelett:
template <class T> void Spai<T>::SPAI_Algorithm(...) { //Iterating over each column for (int col = 0; col < A->n; col++) SPAI_Column(...); } template <class T> void Spai<T>::SPAI_Column(...) { // Variablen werden angelegt //HIer sind einige Funktionen vor der for-schleife for (int step = -1; step < max_impr_steps; step++) { //hier sind einige funktionen die beide fälle gemeinsam haben // HIER!!!!!!!!!!!!!!!!!!!!!!!!!!! will ich jetzt für den Fall B einen // Cache bauen, dass wollte ich über eine virtuelle Methode machen //ABER: Dazu müsste ich ja allgemein einen cache besitzen der auch // im Fall A da ist aber halt dann NULL oder empty oder so... // An die folgende Submethode sollte dieser Cache //dann weitergegeben werden, die ja virtuell ist und man dann dort // machen kann was man will Approximative_Solution(...) // ....ab hier folgen noch mehrere Fkt-Aufrufe die beide fälle gemeinsam haben } }hier Fall A
template <class T> class Spai_Standard : public Spai<T> { public: private: protected: virtual void Approximative_Solution(...); };//hier Fall B
template <class T> class Spai_Caching : public Spai<T> { public: private: protected: virtual void Approximative_Solution( ...); };Und hier die virtuelle Methode
template<class T> void Spai_Caching<T>::Approximative_Solution(...) { //Hier will ich jetzt einen Cache verwenden, //da wir uns sowieso schon im Fall B befinden //ist das also jetzt kein Problem - nur muss ich //dazu wohl den cache an diese Methode übergeben.