我在main()中重构了500行的C++代码来求解微分方程.我想把我们的求解器的重要思想封装成更小的函数(即"SolvePotential(...)"而不是50行的数字代码).
我应该用一堆函数来编写这个序列,这些函数需要非常长的参数列表,例如:
int main(int *argc, void **argv){
interpolate(x,y,z, x_interp, y_interp, z_interp, potential, &newPotential);
compute_flux(x,y,z, &flux)
compute_energy(x,y,z, &eng)
...
// 10 other high-level function calls with long parameter lists
...
return 0;
}
Run Code Online (Sandbox Code Playgroud)
或者我应该创建一个像这样调用的"SolvePotential"类:
int main(int *argc, void **argv){
potential = SolvePotential(nx, ny, nz, nOrder);
potential.solve();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我将在SolvePotential中定义使用成员变量而不是长参数列表的函数,例如:
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)
}
Run Code Online (Sandbox Code Playgroud)
在任何一种情况下,我都怀疑我会非常重复使用这些代码......实际上,我只是在重构以帮助提高代码清晰度.
也许这就像争论"它是'12'还是'打了十几个'?",但你怎么看?
两者都不。“将我的所有代码从一个函数移至一个类”不是 OOP。OOP 的基本规则之一是一个类应该有一个单一的职责范围。这不是一个单一的职责,大约有 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)
}
Run Code Online (Sandbox Code Playgroud)
这也使得维护类不变性变得几乎不可能,不是吗?什么时候调用compute_flux有效?解决?插?什么可以阻止我以错误的顺序做这件事?如果我这样做,班级会处于有效状态吗?我能从中获得有效数据吗?
然而,为什么是非此即彼呢?为什么不能创建多个类和函数?
// 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
Run Code Online (Sandbox Code Playgroud)
当然,这些函数不一定是函数。如果必要/方便,每个都可以成为一个类,或者更好的是,成为一个函子,以维持必要的状态,并且也许允许您将它们作为参数有效地传递给其他函数。
如果最佳性能很重要,您可能必须小心直接返回较大的结构,而不是使用输出参数,但我肯定会首先进行分析,看看它是否是一个问题,即使是,您也可以避免带有表达式模板的输出参数。
如果您有一个可以执行许多独立操作的概念对象,则可能暗示您需要一些 OOP,它应该建模为具有许多成员函数的类,当然每个成员函数都维护该类不变,无论它们如何、何时以及为何被调用。
如果您需要组合多个函数,将它们粘合在一起以形成新的、更大的功能块,那么函数式编程和函子很可能就是您所需要的。需要可组合函数的一个常见原因(但绝对不是唯一的原因)是,如果您需要对许多不同的数据集(甚至可能是几种不同的类型,全部实现相同的概念)执行相同的操作。让函子完成繁重的工作允许它与 std::transform 或 std::for_each 一起使用。您可能还想使用柯里化来逐步组装函数(也许某些函数可以使用一组固定参数进行参数化,这些参数在调用之间不会变化)。再次创建一个用这些固定参数初始化的函子,然后在operator()中提供变化的数据。
最后,如果您只需要对某些可变数据执行一系列操作,那么普通的旧过程编程可能最适合您的需求。
最后,进行泛型编程,对必要的类和函数进行模板化,使它们能够协同工作,而不必跳过指针间接或继承之类的麻烦。
不要太沉迷于 OOP。使用您可以使用的工具。
我对你的问题的上下文了解不够,无法确定,但在我看来,你真正需要的不是一个类,它只是一个函数层次结构。您的用户代码调用solve()。solve() 在内部调用(例如,为了示例而编造的)、interpolate() 和compute_energy()。compute_energy() 内部调用compute_flux(),依此类推。每个函数仅进行几次调用来执行构成该函数职责的逻辑步骤。因此,没有一个地方有一个包含十几个不同职责的庞大类,也没有一个按顺序执行所有操作的大型整体函数。
无论如何,“非常长的参数列表”并没有什么问题(你通常可以通过将其中一些参数组合在一起来缩短它们,但即使你不能,传递大量参数也没有什么“非 OOP” .相反,这意味着该函数与其他所有内容都很好地封装在一起。它所需要的只是在参数中传递,因此它并不真正与应用程序的其余部分相关联。