Как правильно разложить код на части? - PullRequest
2 голосов
/ 16 июля 2011

У меня очень простой вопрос программирования. Представьте, что у меня есть две функции, чье определение практически одинаково, за исключением того, что они изменяются только во внутреннем двоичном условии. Остальной код в обеих функциях практически одинаков.

Чтобы иметь читаемый и простой в обслуживании код, мне было интересно, есть ли лучшее решение, кроме использования параметра 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 ]

4 голосов
/ 16 июля 2011

Я бы также использовал логический параметр, который будет различать их.Я бы не использовал сложный шаблон для этой простой процедуры.

Я бы использовал

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

, чтобы в конечном итоге уменьшить избыточность.Я бы также определил независимые имена

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

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

, потому что считаю параметры флага в API плохими.

3 голосов
/ 16 июля 2011

Несколько человек предложили использовать функторы, и это хороший совет.Замечательно, что эквиваленты функторов == и! = И других подобных операторов сравнения уже существуют как часть стандартной библиотеки 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 можно создать до int и BinaryFunctor до 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>());, и он будет работать как положено.Вы также найдете другие функторы, такие как (опуская пространство имен std): greater, less, greater_equal, less_equal и другие.

3 голосов
/ 16 июля 2011

Вы можете написать это как:

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 голосов
/ 16 июля 2011

Я бы сказал, что вы делаете 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;
}
1 голос
/ 16 июля 2011

Для динамического решения я бы использовал функции указателя.

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

Для статического решения ... Я бы использовал функцию шаблона типа значения.Ex.

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);
0 голосов
/ 16 июля 2011

Вам не нужен шаблон, вам нужна техника рефакторинга.

Я обычно пишу четыре функции, чтобы вы получили

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

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

Вам может потребоваться разделить некоторые переменные между ними в качестве параметров. Может случиться так, что эти функции окажутся полезными, многоразовыми функциями. Скорее всего, вы обнаружите, что требуется больше рефакторинга. Теперь это проще, потому что после извлечения этих функций вам станет более понятно, что они делают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...