Funktionen in Klasse Gruppieren
-
Hallo.
Meine Klassen werden langsam sehr groß und unübersichtlich, obwohl ich Funktionen wenn möglich als private definiere.
Eine Klasse sieht z.B. (vereinfacht) so aus:
class CVariable{ public: CVariable(){} /*Functions for gradient calculation*/ public void CalcGradientLeastSquare(); public void CalcGradientGreenGaussNodeBased(); public void CalcGradientGreenGaussCellBased(); /*...u.v.m...*/ /*Functions for node values*/ public double GetExternalNodeValue(TNode* N); public double GetInternalNodeValueInverseDistance(TNode* N); public double GetInternalNodeValueVolumeWeighted(TNode* N); /*...u.v.m...*/ };Frage: Ist es möglich, die Funktionen zu gruppieren? Alle Funktionen für Gradienten würde ich dann in eine Gruppe GradientFunctions und alle für Knotenwerte in NodalFunctions packen. Der Aufruf würde dann in etwa so aussehen:
CVariable* V = new CVariable(); double x = .0; V->GradientFunctions.CalcGradientLeastSquare(); x = V->NodalFunctions.GetExternalNodeValue(N); /*Usw. ....*/Vielen Dank für Anregungen. Mit dem namespace habe - zumindest ich - es nicht hin bekommen. Auch "Nested class" scheitert irgendwie.
-
"Scheitert irgendwie" - ja dann machst du irgendwie etwas falsch.
-
sieht so aus, als würdest du aus einer anderen sprache kommen.
in C++ ist es häufig üblich, funktionen, die gar nicht auf die interna einer klasse zugreifen müssen, auch nicht als elementfunktionen ("methoden") zu definieren, sondern als freistehende funktionionen. auch etwa so etwas wieoperator+.wenn du z.b. aus Java kommst, ist das sicherlich ungewohnt, das kommt aber mit der zeit - erinnere dich einfach immer an die frage "kann ich xy nicht besser als freie funktion implementieren?"
class Variable { //... }; double calculate_something (const Variable&);das ist zwar schon alt, aber immer noch ganz gut: http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197
wenn du dann immer noch viele (private) funktionen hast, überlege dir, welche davon teil des interfaces für das jeweilige "modul" sind - also was davon soll in den header? du machst - mit deinem header - sozusagen ein commitment, das womöglich auch längerfristig ist: diese funktionen stelle ich allen zur verfügung - und die solltest du dann auch in zukunft unterstützen. auch wenn du der einzige bist, der den code jemals "verwendet", ist so ein gedanke nicht schlecht (für dich selbst). die "anderen" funktionen kannst du in der implementierenden datei in einem unbenannten namespace definieren:
//x.cpp namespace { void foo () { //sichtbar nur in dieser übersetzungseinheit //... } }- aber das ist nur eine von vielen möglichkeiten.
generell sieht es so aus, als müsste sich deine CVariable um zu viel verschiedenes kümmern. ohne genau zu wissen, worum es geht wirkt etwa das:
void CalcGradientLeastSquare(); void CalcGradientGreenGaussNodeBased(); void CalcGradientGreenGaussCellBased();nach überflüssiger wiederholung desselben codes und schreit nach einer verallgemeinerung wie etwa - etwa nur *eine* funktion
CalcGradient, der man als parameter ein funktionsobjekt übergibt, das festlegt, wie der gradient berechnet wird. sozusagen aus "GreenGaussNodeBased" und "GreenGaussCellBased" jeweils eigene "policy"-klassen machen, die das verhalten von CalcGradient spezifizieren. wie gesagt, nur als beispiel ohne genau zu wissen, was du tust.
-
Hallo.
Genau so mache ich es auch.
class CVariable{ private: void CalcGradientLeastSquare(); void CalcGreenGaussNodeBased(); void CalcGreenGaussCellBased(); public: public CVariable(){} public CalcGradient(GradientTypeENUM Type = GradientTypeENUM::LeastSquare){ switch(Type){ case GradientTypeENUM::LeastSquare: CalcGradientLeastSquare(); break; case GradientTypeENUM::GreenGaussNodal: CalculateGradientNodeBased(); default: CalculateGradientGreenGaussCellBased(); break; } } };Aber erstens muss ich viel innerhalb von CVariable programmieren, so dass ich die Private-Funktionen immernoch sehe. Zweitens - ja - die Klasse CVariable ist sehr mächtig. Ich muss, glaub mir das einfach, alle Funktionen dort behalten bzw. wenn ich sie auslagere, wird es für mich noch unübersichtlicher. Es ist einfach sehr bequem am Ende im eigentlichen Programmablauf Variable.CalcGradient() zu schreiben.
Also, ich entnehme Deiner Antwort, dass das wohl nicht geht weiter zu gruppieren.
Und ja: Ich programmiere die GUI in C# und den Kernel (eine DLL) in C++. Kann schon sein, dass ich da ab und an mal durcheinander komme. Aber genau daher kommen eben auch meine vielen Funktionen, da ich sehr viele Werte in der Klasse habe und für alle brauche ich eine GetWert() und eine SetWert() Funktion. Auf diese greife ich dann von C# aus über den InteroptService zu:
extern "C" __declspec(dllexport) int GetValue(CVariable* V){return V->GetValue();}Das Programm hat schon 600 Seiten Umfang, weshalb ich da grundlegend nichts mehr ändern kann/will.
-
Also einerseits glaube ich dem "So mache ich es auch" nicht, zumindest falls du dich damit auf den letzten Absatz von doves Posting beziehen sollte. Dove hatte ja vorgeschlagen, dass das CalcGradient ein Funktionsobjekt zur Berechnung bekommt, du hast aber nur ein switch und machst die Berechnung weiterhin innherhalb deiner Mega-Klasse. Hast du so einen diesen switch noch an anderen Stellen?
Aber erstens muss ich viel innerhalb von CVariable programmieren, so dass ich die Private-Funktionen immernoch sehe. Zweitens - ja - die Klasse CVariable ist sehr mächtig. Ich muss, glaub mir das einfach, alle Funktionen dort behalten bzw. wenn ich sie auslagere, wird es für mich noch unübersichtlicher.
Genau das glaube ich nicht, allerdings ohne deinen Code zu kennen. Eine Klasse sollte eine Sache tun (Single-Responsibility-Prinzip), deine Klasse scheint aber viel mehr zu tun, wenn du schon gruppieren möchtest.
Werden auf einem Objekt eigentlich mehrere der CalcGradient*-Funktionen aufgerufen oder immer nur eine? Diese Funktionen scheinen alle 0 Parameter zu haben und returnen auch nix - sie ändern also nur State. Aber eine Gradientenberechnung hört sich für mich so an, als bräuchte sie gar keinen State zu haben.
Es ist einfach sehr bequem am Ende im eigentlichen Programmablauf Variable.CalcGradient() zu schreiben.
Das ist aber ja unabhängig von den anderen Dingen.
-
eine möglichkeit, nach der du googlen könntest, wäre das sogenannte PIMPL-idiom.
kurz, so:class CVariable { private: struct pimpl; std::unique_ptr<pimpl> impl; public: //special functions (konstruktoren, auch wenn default, nur deklarieren, z.b. CVariable (CVariable&&) noexcept; void some_public_function(); }; //und erst in der .cpp-Datei definieren: CVariable::CVariable (CVariable&&) noexcept = default; struct CVariable::pimpl { Data data; void some_private_function () { /* ... */ } }; void CVariable::some_public_function () { impl->some_private_function(); }der grund, warum pimpl vielleicht nicht angebracht ist, ist, dass du mit einem anderen design insgesamt vielleicht besser aufgehoben gewesen wärst. aber das scheint jetzt wohl schon zu spät.
hier hast du einen überblick über verschiedene möglichkeiten von PIMPL und die pros und cons: https://herbsutter.com/gotw/_100/