面向对象还是顺序? 我重构了主(500)C++代码中的一行,用于求解微分方程。我想将我们的解算器的大思想封装成更小的函数(即“SolvePotential(…)”而不是50行数字代码)

面向对象还是顺序? 我重构了主(500)C++代码中的一行,用于求解微分方程。我想将我们的解算器的大思想封装成更小的函数(即“SolvePotential(…)”而不是50行数字代码),c++,oop,C++,Oop,我是否应该用一系列采用非常长的参数列表的函数顺序编码,例如: 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

我是否应该用一系列采用非常长的参数列表的函数顺序编码,例如:

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;
}    
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)
}
或者我应该创建一个“SolvePotential”类,该类的名称如下:

int main(int *argc, void **argv){
   potential = SolvePotential(nx, ny, nz, nOrder);
   potential.solve();
   return 0;
}
我将在SolvePotential中定义使用成员变量而不是长参数列表的函数,例如:

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;
}    
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)
}
不管是哪种情况,我都怀疑我是否会反复使用代码。。。真的,我只是在重构,以帮助代码清晰


也许这就像是在争论“它是‘12’还是‘12’?”,但你怎么看?

按顺序写,然后重构,如果你认为有什么东西可以重用或者可以让它更清晰


另外,SolvePotential类没有太大意义,因为类应该是具有SolvePotential方法的对象。

我赞成该类,因为它将数据包装在一个更整洁的包中,并且使main()函数非常清晰


在某种意义上,您已经清理了main()函数,现在有了一个凌乱的类,您可以根据自己的判断进一步清理它。一种分而治之的方法。或者是一种“把我所有的垃圾塞进阁楼”的方法,至少房子最常用的部分是干净的。

既然你使用的是面向对象的语言,你应该选择对象,假设你将来可以重用这些对象


只有一个建议:尝试设计一个好的类图。你的程序有哪些实体?我会看到一个“等式”类和一个派生的“微分等式”类,依此类推。

因为此时您不希望在其他地方重用代码,所以我将重点放在使代码可读性和干净性上。这样,您就可以知道一年后当您需要再次求解微分方程时,或者当您发现确实需要重用代码时,它会做什么。现在,带参数列表的函数似乎是一个很好的方法。如果您发现它变得太笨拙,或者您确实需要重用它,那么在这一点上走对象路线是很有意义的。

“SolvePotential”是一个动词,类往往是附加动词的名词。我对您的问题的细节了解不多,但这可能是一个迹象,表明程序方法比OO更清晰。在任何情况下,如果您确实创建了这个类,它只不过是为函数打包而已


除非我有第二个位置使用这个类,否则我只需用显式的参数来声明函数——这将比使用隐藏状态的类的方法更清楚(尤其是对于一个新的人来看这个C++代码)。

< P>实际上C++不仅仅是一个OO语言,它混合了其他的范例,包括程序性的。能够使用类并不能使它们更适合任何问题

在我看来,函数在这里更有意义,因为您正在实现不基于状态且不需要重用数据的数学过程。 这里使用OO意味着构造对象只是为了调用一个方法,然后销毁它们。这听起来比过程API更容易出错,更不直观。另外,正如bradheintz所说,一个显式的参数列表也消除了在实际使用类之前必须记住初始化类的问题(重构时的一个典型错误)

顺便说一下,在函数方面,使用返回值而不是i/o参数通常会使API看起来更清晰

我甚至敢说,您可能想要混合OO和过程,使用类来表示概念之类的向量(我看到了一些x,y,z)。这也会删除一些参数,如果这是您非常关心的

float SolvePotential(const Vector3& vn, float nOrder)
{
    // ...
    const float newPotential = interpolate(vn, v_interp, potential);
    const float flux         = compute_flux(vn);
    const float energy       = compute_energy(vn);
    // ...
    return result;
}
最后,你没有提到性能,所以我想你不介意。但是如果您这样做了,在这种情况下,使用过程方法比使用OO更快、更干净地完成任务似乎更容易


希望有帮助

如果您要进行严格的MartinFowler式重构,那么您就走上了正确的道路。为您的代码寻找隐喻,定义职责并创建遵循这些划分的类。如果使用清晰易懂的名称创建类和成员,这将使代码更具可读性

您可能希望创建一些参数对象来传递参数,而不是创建一长串参数

我在Java中工作了很长时间,现在在C/C++中工作,处于类似的位置。你有没有检查过这是否是一个500行的主方法?实例化对象和检查虚拟表等都有开销。性能是一个问题吗?它似乎会出现在这样的数学计算中。如果是这样的话,那么所有的赌注都将以马丁·福勒式的方式结束


祝您好运。

您忽略了一个中间点,即开始编写对象,但向其方法传递参数。伪代码:

SolverPotential::solve(a, b, c, d){
  SolvePotential::interpolate(a, b);
  SolverPotential::compute_flux(b, c);
  SolverPotential::compute_energy(c, d)
通过这种方式,您可以从“解决这一步需要什么”的顺序模式(可能更简单)开始重构。此外,您可能会看到一系列的参数和检索,这些参数和检索建议对象划分(“在这一步之后,我再也不用‘a’了。也许前两步应该封装在另一个类中。”)

此外,当你只有一大包实例变量时,并行化就更难了。如果你用显式参数开始工作,你会有更好的依赖性,你可能会发现无依赖性的步骤很容易并行化

如果您有大量的参数,那么重构实例变量(和多个变量)是有意义的
PotentialSolution solve()
public class ValuePlusOne implements Computable {
    private int value;
    private int result;
    private Boolean hasRun;
    private static Map instanceMap = new HashMap();

    // Creates an instance reusing an existing one if possible
    public static getInstance(int value) {
        ValuePlusOne instance = (ValuePlusOne)instanceMap.get(value);

        if (instance = null) {
            instance = new ValuePlusOne(value);
            instanceMap.put(value,instance);
        }
        return instance;
    }

    // Private constructor
    private ValuePlusOne(int value) {
        this.value = value;
        hasRun = false;
    }

    // Computes (if not already computed) and returns the answer
    public int compute() {
        if (!hasRun) {
            hasRun = true;
            result = value + 1;
        }

        return result;
    }
}