优点&回调(std :: function / std :: bind)与接口(抽象类)的缺点

时间:2014-03-12 20:08:25

标签: c++ c++11 interface callback boost-asio

我正在使用Boost.Asio在C ++ 11中创建服务器应用程序。我创建了一个类Server,它负责接受新的连接。它基本上只是:

void Server::Accept() {
  socket_.reset(new boost::asio::ip::tcp::socket(*io_service_));
  acceptor_.async_accept(*socket_,
                         boost::bind(&Server::HandleAccept, this, boost::asio::placeholders::error));
}

void Server::HandleAccept(const boost::system::error_code& error) {
  if (!error) {
    // TODO
  } else {
    TRACE_ERROR("Server::HandleAccept: Error!");
  }
  Accept();
}

我找到了两种方法(我确信还有更多方法)可以“修复”TODO注释,即将套接字移动到应该去的位置。在我的情况下,我只想将它返回到拥有Server实例的类实例(然后将其包装在Connection类中并将其插入到列表中。)

  1. Server在其构造函数中有一个参数:std::function<void(socket)> OnAccept,在HandleAccept中调用。
  2. 我创建了一个抽象类IServerHandler或者其他什么,它有一个虚方法OnAcceptServer在其构造函数中将IServerHandler作为参数,拥有服务器实例的类实例扩展IServerHandler并使用Server构造*this作为参数。
  3. 选项1与选项2的优缺点是什么?还有更好的选择吗?我的Connection班级(OnConnectionClosed)遇到了同样的问题。此外,根据我决定如何设计系统,可能需要OnPacketReceivedOnPacketSent回调。

4 个答案:

答案 0 :(得分:44)

我非常喜欢第一种方式,原因如下:

  • 通过接口/类层次结构表示概念/功能使得代码库不那么通用,灵活,并且在将来更难以保持或扩展。这种设计对类型(实现所需功能的类型)施加了一系列要求,这使得将来很难修改,并且在系统更改时最容易失败(考虑在基类被修改时会发生什么)这种类型的设计)。

  • 你所谓的回调方法就是鸭子打字的典型例子。服务器类只需要一个可调用的东西来实现所需的功能,仅此而已,。否&#34;您的类型必须与此层次结构相关联,并且条件是必需的,因此实现处理的类型是完全免费的

  • 另外,正如我所说,服务器只需要 一个可调用的东西 :它可以是具有预期功能签名的任何东西。这为用户在实现处理程序时提供了更大的自由度。可以是全局函数,绑定成员函数,函子等。

以标准库为例:

  • 几乎所有标准库算法都基于迭代器范围。 C ++中没有iterator接口。迭代器只是实现迭代器行为的任何类型(可解除引用,可比较等)。迭代器类型是完全自由的,不同的和解耦的(未锁定到给定的类层次结构)。

  • 另一个例子可能是比较器:比较器是什么? 只是具有布尔比较函数签名的任何东西,可调用的东西,它接受两个参数并返回一个布尔值,表示两个输入值是否相等(小于,大于等)特定比较标准的观点。 没有Comparable界面。

答案 1 :(得分:1)

请注意,在许多情况下,您会对特定类型进行PREFER绑定 因此,在这种情况下,声明您的类必须具有IServerHandler可以帮助您和其他开发人员了解他们应该实现哪些接口以便与您的类一起工作。
在将来的开发中,当您向IServerHandler添加更多功能时,您会强制您的客户(即派生类)跟上您的开发。
这可能是理想的行为。

答案 2 :(得分:1)

这一切都归结为你的意图。

一方面,如果您希望期望某个功能属于特定类型,那么它应该根据它的层次结构来实现,例如虚函数或成员指针,在这种意义上的限制是好的,因为它有助于使您的代码易于正确使用,并且难以正确使用。

另一方面,如果你只是想要一些抽象的“去这里做这个”功能,而不必担心它与特定基类紧密耦合的任何负担,那么显然别的东西会更合适像指向自由函数或std :: function等的指针

所有这些都更适合您软件的任何特定部分的特定设计。

答案 3 :(得分:0)

你使用什么版本的助推器?恕我直言的最佳方式是使用协同程序。代码将会更加容易。跟随。它看起来像同步代码,但现在我无法进行比较,因为我是从移动设备写的。