我正在开发一些应用程序,需要做出一些架构决策。我遇到的问题是我将不得不使用INTERFACE的规范。 让我们说,目前我只知道INTERFACE将有两个功能,' work'和'打印'。目前我只能猜测这些函数需要什么参数,而且很可能参数将完全不同(例如,我不能有一个容器来存储这些函数的函数指针)。
该项目可能会持续一段时间,在此期间我使用的INTERFACE会越来越明确(我的意思是它可以经常更改)。我想要实现的是以某种方式保护自己免受这些变化的影响,即最小化界面变化时需要完成的工作量。并准备好自己可能出现在INTERFACE中的新功能。
我开始考虑在我的代码中使用某种机制来帮助我这样做。我希望这个机制从INTERFACE调用函数。我希望这个机制摆脱愚蠢无休止的if-else语句。我希望有一种机制,我可以在测试中模拟它与INTERFACE分开。
我读到了可能的调度机制,我读到了关于元编程,boost :: hana,mpl。我读到了关于在boost :: variant或boost :: any中存储函数,以及将元组传递给函数并在index_sequence的帮助下解压缩它们的内容。但是这个解决方案对我来说都不是很好和优雅。
我发明了下面这样的东西,虽然它也远非完美。但我想问你对这种方法的看法。我的情况好吗?它可以维持吗?可扩展?优雅?
#include <iostream>
#include <functional>
using namespace std;
struct work_tag {};
struct print_tag {};
// --- real functions of the INTERFACE ---
void doWork(int a, int b, string s)
{
cout << a << ", " << b << ", " << s << endl;
}
void doPrint(int a, double d, double e, int f, string s)
{
cout << a << ", " << d << ", " << e << ", " << f << ", " << s << endl;
}
// --- fake function for tests ---
void fakeDoWork(int a, int b, string s)
{
cout << "this is a fake: " << a << ", " << b << ", " << s << endl;
}
void fakeDoPrint(int a, double d, double e, int f, string s)
{
cout << "this is a fake: " << a << ", " << d << ", " << e << ", " << f << ", " << s << endl;
}
// --- my class, it will do some operations and call the INTERFACE ---
struct B
{
public:
B(auto funForWorking, auto furForPrinting)
: m_funForWorking(funForWorking), m_funForPrinting(furForPrinting)
{}
template <class... Args>
void request(Args... args)
{
dispatch(std::forward<Args>(args)...);
}
private:
template <class... Args>
void call(work_tag, Args... args)
{
m_funForWorking(std::forward<Args>(args)...);
}
template <class... Args>
void call(print_tag, Args... args)
{
m_funForPrinting(std::forward<Args>(args)...);
}
template <class TYPE_TAG, class... Args>
void dispatch(TYPE_TAG type_tag, Args... args)
{
call(type_tag, std::forward<Args>(args)...);
}
private:
std::function<decltype(doWork)> m_funForWorking;
std::function<decltype(doPrint)> m_funForPrinting;
};
int main()
{
// production code
B b(doWork, doPrint);
b.request(work_tag{}, 1, 2, "a");
b.request(print_tag{}, 1, 2.0, 3.0, 4, "bbb"s);
// somewhere in the tests
B btest(fakeDoWork, fakeDoPrint);
btest.request(work_tag{}, 1, 2, "a");
btest.request(print_tag{}, 1, 2.0, 3.0, 4, "bbb"s);
}
我知道这不是一个完美的解决方案,因为当一个新功能出现在INTERFACE中时,我将不得不修改我的类的代码,添加新的重载&#39; call&#39;,新成员存储新功能,添加新的调度标签。但至少我对INTERFACE现有功能的参数列表的更改有所保护(我只需更改代码中的调用位置)。
理想情况下,我想摆脱提供存储INTERFACE的每个功能的成员的必要性(以某种方式将功能存储在地图中并使用地图分派呼叫)以及提供&#的重载的必要性39;调用&#39;对于INTERFACE的每种方法。但是,在我的情况下,当每个函数都有不同的参数时,我不知道如何做到这一点。
有没有更好的解决方案呢?