C++ рефакторинг общего кода с одним другим оператором
У меня есть два метода f(vector<int>& x, ....) and g(DBConn& x, ....)
где (....) параметры все идентичны.
Код внутри двух методов полностью идентичен, за исключением одного оператора, в котором мы выполняем различные действия в зависимости от типа x:
in f(): we do x.push_back(i)
in g(): we do x.DeleteRow(i)
Какой самый простой способ извлечь общий код в один метод и при этом иметь два разных оператора?
Я имею в виду наличие шаблонного функтора, который перегружает operator () (int a), но это кажется излишним.
6 ответов
Вы можете написать простой адаптер с двумя реализациями, каждая из которых вызывает нужный метод другого класса.
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); }
}
common_function(....)
{
}
f(vector<int>x,... )
{
x.push_back(i);
common_f(...);
}
g(DBConn& x, ....)
{
x.DeleteRow(i);
common_f(...);
}
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);
...
}
ПРИМЕЧАНИЕ: у меня может быть неправильный синтаксис, но вы поняли идею.
Еще одна идея:
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, ...);
}
Классический чехол для функтора:
#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);
}
Вы можете создать функцию, имеющую параметры того, что вы называете (...), и эта функция может реализовать логику, одинаковую для f () и g (). Затем вы можете изменить реализацию функций f () и g (), чтобы вызывать эту новую функцию вместо дублирования логики. Будьте осторожны, если вы делаете что-то дублированное до и после ваших уникальных строк. В этом случае вам могут понадобиться две функции. Во всяком случае, я думаю, что это было бы предпочтительнее, чем дублированные блоки кода.