2010-06-02 7 views
2

J'ai deux méthodes f(vector<int>& x, ....) and g(DBConn& x, ....) où les paramètres (....) sont tous identiques.Code commun du refactor C++ avec une instruction différente

Le code à l'intérieur des deux méthodes sont complètement identiques, sauf pour une déclaration où nous ne différentes actions en fonction du type de x:

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

Quelle est la façon la plus simple d'extraire le code commun dans une méthode et pourtant ont les deux déclarations différentes?

Je pense à un foncteur basé sur un modèle qui surchargerait operator() (int a) mais qui semble trop puissant.

+0

Peut-différemment le code jamais évoluer pour les deux? Si oui, laissez-les comme ils sont, car leur similitude est juste une coïncidence. – JRL

Répondre

4

Vous pouvez écrire un adaptateur simple avec deux implémentations appelant chacune la méthode souhaitée d'une classe différente.

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

Je préfère ce sur les modèles pour deux raisons. Premièrement, il ne générera que du code compilé pour une instance de la fonction f/g, les templates généreront normalement du code compilé pour chaque instanciation de template. Deuxièmement, je vois des modèles comme «faire la même chose quel que soit le type», mais c'est «faire une chose différente selon le type». – Skizz

+0

Je ne vais pas pour cela car il ajoute le prochain niveau d'indirection avec appel de fonction virtuelle. Mais encore c'est une solution valide. – Tomek

+0

@Tomek, une solution de la vie réelle est toujours un compromis :-) –

0

Vous pouvez créer une fonction qui a les paramètres de ce que vous appelez (...), et cette fonction peut implémenter la logique qui est la même dans f() et g(). Vous pouvez ensuite modifier l'implémentation de f() et g() pour appeler cette nouvelle fonction au lieu de dupliquer la logique. Soyez prudent si vous faites quelque chose en double avant et après vos lignes uniques. Vous devrez peut-être deux fonctions dans ce cas. En tout cas, je pense que ce serait préférable d'avoir des blocs de code dupliqués.

6
common_function(....) 
{ 
} 

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

+1. C'est généralement le moyen le plus simple de le faire. – Brian

+0

Cela peut fonctionner, sauf que je devrai le format suivant: 'common_g() x.push_back (i) common_f()' Pas aussi simple. – user231536

+0

De plus, vous avez un ensemble supplémentaire (ou deux) de paramètres poussant. – 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); 
    ... 
} 

OU:

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); 
    ... 
} 

NOTE: Je pourrais avoir une syntaxe mal, mais vous voyez l'idée.

1

Encore une autre idée:

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

cas classique pour un foncteur:

#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

Ce serait encore mieux avec C++ 0x et lambdas ... Vous pouvez probablement éviter les foncteurs alors. – Tomek

Questions connexes