2010-06-02 2 views
2

У меня есть два метода: f(vector<int>& x, ....) and g(DBConn& x, ....) где параметры (....) идентичны.C++ refactor common code with one different statement

Код внутри этих двух методов полностью идентичны для одной инструкции где мы разные действия в зависимости от типа х, за исключением:

in f(): we do x.push_back(i) 
in g(): we do x.DeleteRow(i) 

Что самый простой способ извлечь общий код в один метод и все же имеют два разных утверждения?

Я подумываю иметь шаблонный функтор, который перегружает operator() (int a), но это кажется излишним.

+0

Может код когда-либо эволюционируют по-разному для двоих? Если это так, оставьте их такими, какими они похожи, поскольку их сходство просто случайно. – JRL

ответ

4

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

class MyInterface { 
public: 
    virtual doIt(int i) = 0; 
} 

class VectorImp : public MyInterface { 
public: 
    vector<int>& v; 
    VectorImp(vector<int>& theVector) : v(theVector) {} 
    doIt(int i) { x.push_back(i); } 
} 

class DbImp : public MyInterface { 
public: 
    DBConn& c; 
    VectorImp(DBConn& conn) : c(conn) {} 
    doIt(int i) { c.DeleteRow(i); } 
} 
+0

Я предпочитаю это по шаблонам по двум причинам. Во-первых, он будет генерировать только скомпилированный код для одного экземпляра функции f/g, шаблоны обычно генерируют скомпилированный код для каждого экземпляра шаблона. Во-вторых, я вижу шаблоны как «делать то же самое независимо от типа», но это «делать другую вещь в зависимости от типа». – Skizz

+0

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

+0

@Tomek, реальное решение всегда является компромиссом :-) –

0

Вы можете создать функцию, которая имеет параметры того, что вы называете (...), и эта функция может реализовать логику, которая является одинаковой в f() и g(). Затем вы могли бы изменить реализацию f() и g() для вызова этой новой функции вместо дублирования логики. Будьте осторожны, если вы делаете что-то дублирующееся до и после ваших уникальных линий. В этом случае вам могут понадобиться две функции. Во всяком случае, я думаю, что было бы предпочтительнее иметь дублированные блоки кода.

6
common_function(....) 
{ 
} 

f(vector<int>x,...) 
{ 
    x.push_back(i); 
    common_f(...); 
} 
g(DBConn& x, ....) 
{ 
    x.DeleteRow(i); 
    common_f(...); 
} 
+1

+1. Обычно это самый простой способ сделать это. – Brian

+0

Это может работать, за исключением того, что мне нужно будет следующий формат: 'common_g() x.push_back (я) common_f()' не так просто. – user231536

+0

Кроме того, у вас есть дополнительный набор (или два) нажатия кнопки. – Skizz

1
template<class T> 
struct Adapter; 

template<> 
struct Adapter<vector<int> > 
{ 
    static void execute(vector<int> &x, int i) 
    { 
    x.push_back(i); 
    } 
}; 

template<> 
struct Adapter<DBConn> 
{ 
    static void execute(DBConn &x, int i) 
    { 
    v.DeleteRow(i); 
    } 
}; 

template<class T> 
void f(T &t, ...) 
{ 
    ... 
    Adapter<T>::execute(t, i); 
    ... 
} 

ИЛИ:

template<class T> 
struct adapter_traits; 

template<> 
struct adapter_traits<vector<int> > 
{ 
    typedef void (vector<int>::*PMF)(int); 
    static const PMF pmf = &vector<int>::push_back; 
} 

template<> 
struct adapter_traits<DBConn> 
{ 
    typedef void (DBConn::*PMF)(int); 
    static const PMF pmf = &DBConn::DeleteRow; 
} 

template<class T> 
void f(T &t, ...) 
{ 
    ... 
    (t.*adapter_traits<T>::pmf)(i); 
    ... 
} 

Примечание: Я мог бы иметь некоторый синтаксис неправильно, но вы получите идею.

1

Еще одна идея:

template<class T> 
void f(T &t, void (T::*p)(int), ...) 
{ 
    ... 
    (t.*p)(i); 
} 

void g() 
{ 
    DBConn x; 
    vector<int> y; 
    f(x, &DBConn::DeleteRow, ...); 
    f(y, &vector<int>::push_back, ...); 
} 
1

Классический случай для функтора:

#include <vector> 
#include <DBConn.h> 

// T: The type of the object that is to be manipulated. 
// A: The type of the object that will do the manipulating 
//  This may be a functor object or a function pointer. 
// 
// As this is a template function the template parameters will 
// be deduced by the compiler at compile time. 
template<typename T,typename A> 
void action(T& obj,A const& action/*,....*/) 
{ 
    // Do Stuff 
    action(obj,5); 
    // Do more Stuff 
} 

// Functor object 
struct MyVectorAction 
{ 
    // Just defines the operator() 
    // Make sure it is a const method. 
    // This does the unique bit of code. The parameters should be what you pass into action 
    void operator()(std::vector<int>& data,int val) const {data.push_back(val);} 
}; 
void f(std::vector<int>& x) 
{ 
    action(x,MyVectorAction()/*.... Params ....*/); 
} 


struct MyDBConnAction 
{ void operator()(DBConn& data,int val) const {data.DeleteRow(val);} }; 
void g(DBConn& x) 
{ 
    action(x, MyDBConnAction()); 
} 

int main() 
{ 
    std::vector<int> x; 

    f(x); 
} 
+0

Было бы еще лучше с C++ 0x и lambdas ... Тогда вы, вероятно, можете избежать функторов. – Tomek