如何正确分解代码?

时间:2011-07-16 17:00:36

标签: c++ function methods

我有一个非常基本的编程问题。想象一下,我有两个函数,它们的定义实际上是相同的,只是它们只在内部二进制条件下变化。两个函数中的其余代码几乎相同。

为了拥有易读且易于维护的代码,我想知道除了使用选择这些函数的操作模式的bool参数之外是否还有更好的解决方案?那有design-pattern吗?

在下面的代码中,我用两个名为doA()doB()的函数来说明我的疑问。点(....)对应于两个函数完全相同的代码。 我创建了一个带有附加布尔参数的新doNew()函数来选择适当的功能。但请注意,尽管这是一种可能的解决方案,但由于if条件的主体内部存在重复的代码,因此效率仍然低下。

void doA( ..... ){
     .....
     .....
         if(x!=y){
             ....
             ....
             ....
         }
     .....
}

void doB( ..... ){
     .....
     .....
         if(x==y){
             ....
             ....
             ....
         }
     .....
}

void doNew( ....., bool selectionMode ){
     .....
     .....
         if(selectionMode == true){
             if(x==y){
                 ....
                 ....
                 ....
             }
         }
         else{
             if(x!=y){
                 ....
                 ....
                 ....
             }

         }
     .....
}

6 个答案:

答案 0 :(得分:4)

我还会使用一个布尔参数来区分它们。对于那个简单的程序,我不会使用复杂的模式。

我会用

void doIt(..., BOOL is_equal) {
   ...
   if((a == b) == is_equal) { // or: is_equal ^ (a == b)
      ...
   }
   ...
}

最终减少冗余。我还会定义独立的名字

void doA(...) {
    doIt(..., true);
}

void doB(...) {
    doIt(..., false);
}

因为我认为API中的标志参数不好。

答案 1 :(得分:3)

我想说你将do作为模板函数,将二元函数作为函数的函子参数。类似的东西:

template <class BinaryFunctor>
void do(...,BinaryFunctor f)

这个二元仿函数会返回bool,您可以在do内使用。

仿函数的示例代码:

struct Equals
{
    Equals(int x , int y) : m_x(x), m_y(y){}
    bool operator()() const { return m_x == m_y;}

private:
    int m_x;
    int m_y;
};
struct NotEquals
{
    NotEquals(int x , int y) : m_x(x), m_y(y){}
    bool operator()() const { return m_x != m_y;}

private:
    int m_x;
    int m_y;
};
template<class BinaryFunctor>
void doSomething(BinaryFunctor f)
{
    if(f())
    {
        //Condition satisfied
    }
}
int main () 
{
    doSomething(Equals(10,11));
    doSomething(NotEquals(10,11));
    return 0;
}

答案 2 :(得分:3)

您可以将其写为:

if(selectionMode && x==y || !selectionMode && x!=y)
    //....

还有其他方法可以做到这一点。您可以传递一个仿函数(例如:一个函数指针)进行比较。您可以使用仿函数模板参数编写模板,并使用模板定义这两个函数。

更新

人们提供了几个示例,因此我将向您展示另一个示例,这个示例使用普通函数指针,并且不会传递两个值进行比较,但会传递比较结果。 (注意:其他示例是首选,但您看到的示例越多越好)

//type of the function pointer
typedef bool (*dofuncptr)(bool);

bool do_istrue(bool b) {
    return b;
}

//negates the input
bool do_isfalse(bool b) {
    return b;
}

void doX(dofuncptr fun) {
    //...
    if (fun(x == y)) {
        //....
    }
    //...
}

int main() {
    //you can use it like this:
    doX(&do_istrue);
}

答案 3 :(得分:3)

许多人建议使用仿函数,这是一个很好的建议。对你来说最棒的是,==和!=的算子等价物和其他这样的比较运算符已经作为标准C ++库的一部分存在。我就是这样做的:

#include <functional>
#include <iostream>

template <class T, template <class T> class BinaryFunctor>
void doSomething(T x, T y, BinaryFunctor<T> f) {
  if (f(x, y)) {
    std::cout << "True" << std::endl;
  } else {
    std::cout << "False" << std::endl;
  }
}

int main(int argc, const char* argv[])
{
  doSomething(5, 5, std::equal_to<int>());
  return 0;
}

doSomething模板函数接受两个相同类型T的参数和一个类型为BinaryFunctor<T>的参数。请注意,T出现在所有三个参数中,因此在所有传递的参数中必须相同。因此传递两个int和一个std::equal_to<int>很好(正如我在示例中所做的那样),因为T可以实例化为intBinaryFunctor可以{ {1}}。

std::equal_to是标准库的一个比较函数对象(或仿函数)的示例。它只是一个覆盖std::equal_to的类,因此可以使用operator()类型的对象,就像它是一个真正的函数一样。因此,当std::equal_to类型的对象作为std::equal_to参数传递给函数时,您可以像f一样使用它。

现在,如果您想将比较运算符更改为f(someInt, anotherInt),则只需将函数调用更改为!=,它将按预期工作。您还可以找到其他仿函数,例如(省略doSomething(5, 5, std::not_equal_to<int>());命名空间):stdgreaterlessgreater_equal等。

答案 4 :(得分:1)

对于动态解决方案,我会使用指针函数。

void(*doNew)(...);
doNew = &doA;
doNew(...);//now calls doA
doNew = &doB;
doNew(...);//now calls doB

对于静态解决方案...... 我会使用值类型模板函数。 防爆。

template<bool TMode>
void doNew( .....);
template<>
void doNew<TRUE>( .....);
{
     .....
     .....
             if(x==y){
                 ....
                 ....
                 ....
             }
     .....
     .....
}
template<>
void doNew<FALSE>( .....);
{
     .....
     .....
             if(x!=y){
                 ....
                 ....
                 ....
             }
     .....
     .....
}

然后你就可以像......一样使用它。

doNew<FALSE>(...); // is equivalant to doNew(..., false);

答案 5 :(得分:0)

你不需要一个模式,你需要一种重构技术。

我倾向于写四个函数,以便你得到

DoA()
{
   Part1();
   SpecificForA();
   Part2();
};

DoB()
{
   Part1();
   SpecificForB();
   Part2();
};

您可能需要在它们之间共享一些变量作为参数。这些功能可能是有用的,可重复使用的功能。更有可能的是,您发现需要进行更多的重构。现在这更容易了,因为在提取这些功能之后,您可以更清楚地了解它们的作用。