标记调度以防止接口更改

时间:2017-05-06 19:59:08

标签: c++ boost

我正在开发一些应用程序,需要做出一些架构决策。我遇到的问题是我将不得不使用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的每种方法。但是,在我的情况下,当每个函数都有不同的参数时,我不知道如何做到这一点。

有没有更好的解决方案呢?

0 个答案:

没有答案