Weder noch. "Den gesamten Code aus einer einzigen Funktion in eine einzige Klasse zu verschieben, ist kein OOP. Eine der grundlegenden Regeln von OOP ist, dass eine Klasse Folgendes haben sollte ein einziger Zuständigkeitsbereich . Es handelt sich nicht um eine einzige Verantwortung, sondern um etwa 15:
SolverPotential::solve(){
SolvePotential::interpolate()
SolverPotential::compute_flux()
SolverPotential::compute_energy()
// ...
// 10 other high-level function calls with NO parameter lists (just use private member variables)
}
Das macht es auch so gut wie unmöglich, eine Klasseninvariante beizubehalten, nicht wahr? Wann ist es gültig, compute_flux aufzurufen? Lösen? Interpolieren? Was soll mich davon abhalten, es in der falschen Reihenfolge zu tun? Wird sich die Klasse in einem gültigen Zustand befinden, wenn ich das tue? Bekomme ich gültige Daten aus ihr heraus?
Aber warum ist es ein Entweder-oder? Warum kann man nicht mehrere Klassen et Funktionen?
// This struct could be replaced with something like typedef boost::tuple<double,double,double> coord3d
struct coord3d {
double x, y, z;
};
coord3d interpolate(const coord3d& coord, const coord3d& interpolated, double potential); // Just return the potential, rather than using messy output parameters
double compute_flux(const coord3d coord&flux); // Return the flux instead of output params
double compute_energy(const coord3d& coord); // And return the energy directly as well
Natürlich müssen diese Funktionen nicht zwangsläufig Funktionen sein. Wenn nötig/zweckmäßig, könnte jede zu einer Klasse oder besser noch zu einem Funktor gemacht werden, um den notwendigen Zustand zu erhalten und vielleicht auch, um sie effizient als Argumente an andere Funktionen übergeben zu können.
Wenn optimale Leistung wichtig ist, müssen Sie möglicherweise mit der direkten Rückgabe größerer Strukturen vorsichtig sein, anstatt Ausgabeparameter zu verwenden, aber ich würde auf jeden Fall zuerst ein Profil erstellen, um zu sehen, ob es ein Problem ist, und selbst wenn es so ist, können Sie wahrscheinlich Ausgabeparameter mit Ausdrucksvorlagen vermeiden.
Wenn Sie ein konzeptionelles Objekt haben, auf dem eine Reihe unabhängiger Operationen ausgeführt werden können, ist das wahrscheinlich ein Hinweis darauf, dass Sie etwas OOP brauchen, dass es als Klasse mit einer Reihe von Mitgliedsfunktionen modelliert werden sollte, von denen jede natürlich die Invarianz der Klasse beibehält, egal wie, wann und warum sie aufgerufen werden.
Wenn Sie eine Reihe von Funktionen zusammenstellen und sie zu neuen, größeren Funktionen zusammenfügen müssen, sind funktionale Programmierung und Funktoren wahrscheinlich genau das Richtige für Sie. Ein häufiger Grund (aber sicher nicht der einzige) für den Wunsch nach zusammensetzbaren Funktionen ist, dass Sie dieselbe Operation mit vielen verschiedenen Datensätzen durchführen müssen (vielleicht sogar mit mehreren verschiedenen Typen, die alle dasselbe Konzept implementieren). Wenn ein Funktor die schwere Arbeit übernimmt, kann er mit std::transform oder std::for_each verwendet werden. Sie können auch Currying verwenden, um Ihre Funktionen schrittweise zusammenzustellen (vielleicht können einige der Funktionen mit einer Reihe von festen Parametern parametrisiert werden, die sich zwischen Aufrufen nicht ändern). Auch hier erstellen Sie einen Funktor, der mit diesen festen Parametern initialisiert wird, und liefern dann die variierenden Daten in operator().
Und schließlich, wenn Sie einfach eine Reihenfolge von Operationen auf einige veränderliche Daten, kann einfache alte prozedurale Programmierung das sein, was Ihren Bedürfnissen am besten entspricht.
Und schließlich sollten Sie generisch programmieren, indem Sie die erforderlichen Klassen und Funktionen in Schablonen einbauen, damit sie zusammenarbeiten können, ohne dass Sie sich mit Zeigerumleitungen oder Vererbung herumschlagen müssen.
Machen Sie sich nicht zu viele Gedanken über OOP. Nutzen Sie die Werkzeuge, die Ihnen zur Verfügung stehen.
Ich kenne den Kontext Ihrer Frage nicht gut genug, um das mit Sicherheit sagen zu können, aber mir scheint, dass Sie eigentlich keine Klasse brauchen, sondern nur eine Hierarchie von Funktionen. Ihr Benutzercode ruft solve() auf. solve() ruft intern, sagen wir mal (frei erfunden, um des Beispiels willen), interpolate() und compute_energy() auf. compute_energy() ruft intern compute_flux() auf, und so weiter. Jede Funktion macht nur ein paar Aufrufe, um die logischen Schritte auszuführen, die die Verantwortung der Funktion ausmachen. Sie haben also nirgendwo eine riesige Klasse mit einem Dutzend verschiedener Zuständigkeiten oder eine große monolithische Funktion, die alles sequentiell ausführt.
Auf jeden Fall ist gegen "sehr lange Parameterlisten" nichts einzuwenden (man kann sie in der Regel kürzen, indem man einige von ihnen zusammenfasst, aber selbst wenn das nicht möglich ist, ist es nicht "un-OOP", viele Parameter zu übergeben. Im Gegenteil, es bedeutet, dass die Funktion gut von allem anderen gekapselt ist. Alles, was sie braucht, wird in den Parametern übergeben, so dass sie nicht wirklich mit dem Rest der Anwendung verbunden ist.
1 Stimmen
Es wurde allgemeineres C++ verwendet, so dass diese Frage von der gesamten C++-Gemeinschaft gesehen werden würde.