第二组功能仅在一行代码中有所不同

时间:2016-08-07 08:21:26

标签: c++ performance function code-duplication maintainability

我有两个性能关键的功能:

insertExpensive(Holder* holder, Element* element, int index){
    //............ do some complex thing 1 
    holder->ensureRange(index);//a little expensive
    //............ do some complex thing 2
}
insertCheap(Holder* holder, Element* element, int index){
    //............ do some complex thing 1
    //............ do some complex thing 2
}

如何将2个功能组合在一起以提高可维护性?

我糟糕的解决方案:

解决方案1。

insertExpensive(Holder* holder, Element* element, int index){
    do1();
    holder->ensureRange(index);//a little expensive
    do2();
}
insertCheap(Holder* holder, Element* element, int index){
    do1();
    do2();
}

这将是丑陋的。 如果do2需要do1的某些局部变量,那么这也是不切实际的。

解决方案2。

insert(Holder* holder, Element* element, int index, bool check){
    //............ do some complex thing 1 
    if(check)holder->ensureRange(index);//a little expensive
    //............ do some complex thing 2
}

每次通话都需要进行条件检查。

解决方案3。(草稿)

template<bool check> insert(Holder* holder, Element* element, int index){
    //............ do some complex thing 1       (Edit2 from do1());
    bar<check>();
    //............ do some complex thing 2       (Edit2 from do2());
}
template <>
inline void base_template<true>::bar() {  holder->ensureRange(index); }
template <>
inline void base_template<false>::bar() {  }

过度杀伤和不必要的复杂性?

编辑1: 方法有多好的标准优先顺序如下: - 1.最佳表现
2.减少代码重复
3.减少总代码行数 4.更容易阅读专家和初学者

编辑2:编辑第3个解决方案。感谢mvidelgauz和Wolf。

5 个答案:

答案 0 :(得分:6)

你的解决方案2实际上还不是那么糟糕。如果此代码在标头内,则将其隐式地视为内联代码。 (我已经明确地写过)如果用true或false调用它,编译器可以删除if语句,尽管它取决于一系列因素来知道它是否会这样做。 (内联后的整体尺寸,常数的可见性,调整......)

        String username;
        int phone1;

        @Override
        public void widgetSelected(SelectionEvent e) {          
            try
            {
            phone1 = Integer.parseInt(phone.getText());
            }
            catch (Exception exe)
            {           
                MessageDialog.openError(shell, "Error","Phone number has to be a number");
                return;
            }

            try
            {
            username = namefield.getText();
               if (username!= "admin") {
                    throw (new Exception());
                }           
            }
            catch (Exception exe)
            {   
                MessageDialog.openError(shell, "Error","The username must be admin");
                return;
            }


            labelresult.setText("The name entered is:"+ username );


            labelresult2.setText("The phone entered is:"+ phone1 );

解决方案3实际上是您想要实现的,因为模板需要为每个不同的调用进行新的函数实例化,因此它将删除死代码。但是,它的编写方式与编写解决方案2的方式非常相似。

inline void insert(Holder* holder,Element* element,int index, bool check){
    do1();
    if (check)
        holder->ensureRange(index);//a little expensive
    do2();
}

如果您有C ++ 17,则不再需要依赖编译器来删除死代码,因为您可以强制它通过constexpr-if跳过某些代码。这种结构将保证删除if语句中的代码,因为它甚至不需要编译。

template <bool check>
inline void insert(Holder* holder,Element* element,int index){
    do1();
    if (check)
        holder->ensureRange(index);//a little expensive
    do2();
}

答案 1 :(得分:2)

insert(Holder* holder,Element* element,int index, bool ensureRange){
    //............ do some complex thing 1 
    if (ensureRange){
        holder->ensureRange(index);//a little expensive
    }
    //............ do some complex thing 2
}

如果您可以在编译时做出决定并希望使用模板:

template<bool check> insert(Holder* holder,Element* element,int index){
    //............ do some complex thing 1;
    if(check)holder->ensureRange(index);
    //............ do some complex thing 2
}


insert<true>(...); //expensive operation
insert<false>(...); //cheap operation

答案 2 :(得分:1)

让我们假设你做了以下结构:

定义两个受保护方法do1do2以及公共抽象方法Insert的类。

class BaseHolder
{
proteted:

void do1(/* complete with necessary parameters*/)
{

}

void do2(/* complete with necessary parameters*/)
{

}

public:
abstract void Insert(Holder* holder, Element* element, int index);

};

class Expensive : BaseHolder
{
public:
void Insert(Holder* holder, Element* element, int index)
{
    do1();
    holder->ensureRange(index);
    do2();
}
};

class Cheap : BaseHolder
{
public:
void Insert(Holder* holder, Element* element, int index)
{
    do1();
    do2();
}
};

很抱歉,如果我犯了一些语法错误,但这是我看到解决方案的方式。

另一种可能性是制作一个自定义CheapExpensive类,它们都包裹Holder,并在Expensive构造函数中执行范围验证:

class Base
{
protected:
Holder* _holder;
public:
Holder* GetHolder(){ return _holder; }
}

class Cheap : Base
{
    public:
    Cheap(Holder* holder)
    {
        _holder = holder;
    }
};

class Expensive : Base
{
    public:
    Expensive(Holder* holder)
    {
         holder->ensureRange(index);
         _holder = holder;
    }
};

使用廉价和昂贵的对象作为Insert方法的参数。 我想第二种解决方案比第一种解决方案更好。

一个更像第一个的解决方案,但它使用了模板方法设计模式,最好是最终:

class BaseHolder
{
proteted:

void do1(/* complete with necessary parameters*/)
{

}

void do2(/* complete with necessary parameters*/)
{

}
virtual void check(Holder* holder, int index){  };
public:
void Insert(Holder* holder, Element* element, int index)
{
    do1();
    check(holder, index);
    do2();    
};

class Expensive : BaseHolder
{
protected:
override void check(Holder* holder, int index)
{ 
    holder->ensureRange(index);
}
};

class Cheap : BaseHolder
{
};

此解决方案仅在check对象上定义Expensive方法,它具有最多的代码重用,并且它绝对是最干净的方法之一。不幸的是,这是关于oop设计而不是关于性能不是最好的,但你必须考虑哪个是你的优先事项,正如你刚刚总结的那样。

答案 3 :(得分:1)

从技术上讲,我更喜欢像mvidelgauz's answer中所示的解决方案,但是由于不能自我解释的文字true或{{{{}}},因此必须实际调用函数时添加的参数看起来很糟糕1}}。所以我将三个函数组合在一起:一个内部(false)函数,它提供了一个参数检查标志,以及两个外部函数,提供了关于函数的明显名称,我更喜欢像private这样的函数。 ,checkedInsert。然后,这些公共函数将调用4参数实现。

在下面的代码片段中减少了大部分参数,使解决方案最为明显。它应该被视为类定义的一个片段:

uncheckedInsert

此解决方案提供:最佳用户界面和一致的实施。
顺便说一句: 我真的怀疑单个条件检查会导致真正的性能问题。

答案 4 :(得分:0)

作为另一种选择,您可以简单地使用默认函数参数,默认值对应于无范围检查

void insert(Holder* holder, Element* element, int index, 
            bool performRangeCheck = false)
{
  /* do some complex thing 1 */

  if(performRangeCheck)
  {
    holder->ensureRange(index);
  }

  /* do some complex thing 2 */
}

// ...

insert(holder, element, 2);       // no range check
insert(holder, element, 2, true); // perform range check