优点& cons的回调(std :: function / std :: bind)vs接口(抽象类) [英] Pros & cons of a callback (std::function/std::bind) vs an interface (abstract class)
问题描述
我使用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
注释,即将套接字移动到任何地方。在我的情况下,我只想让它回到类实例拥有服务器
实例(然后将其包装在连接
类并将其插入列表)。
-
服务器
它的构造函数:std :: function< void(socket)>我创建了一个抽象类,
。
$
IServerHandler
或其他,它有一个虚方法 OnAccept
。 服务器
在其构造函数和拥有服务器实例的类实例中将 IServerHandler
作为参数extends IServerHandler
并以 * this
作为参数构建服务器
。 选项1和选项2的利弊是什么?有什么更好的选择吗?我在我的连接
类( OnConnectionClosed
)有同样的问题。此外,根据我如何决定设计系统,它可能需要 OnPacketReceived
和 OnPacketSent
回调。
我强烈喜欢第一种方式有几个原因:
-
通过接口/类层次结构来表示概念/功能,使得代码库不那么通用,灵活,而且在将来更难以维护或扩展。这种设计对类型(实现所需功能的类型)强加了一组要求,这使得在将来难以修改,并且当系统改变时最容易失败(考虑当基类被修改时发生什么)
-
您所呼叫的回呼方法只是鸭式输入的经典范例。服务器类只需要一个可实现所需功能的可调用事件,没有更多,没有更少。不需要条件,因此实现处理的类型完全免费。
预期的函数签名。这给予用户在实现处理程序时更自由。可以是全局函数,绑定成员函数,函子等。
以标准库为例:
-
几乎所有标准库算法都基于迭代器范围。 C ++中没有
iterator
界面。迭代器只是实现迭代器行为的任何类型(即可解引用,可比较等)。迭代器类型是完全自由的,不同的和解耦的(不锁定到给定的类层次结构)。 -
另一个例子可能是比较器:比较器是什么? 只是具有布尔比较函数签名的任何东西,可调用的函数需要两个参数,并返回一个布尔值,表示两个输入值是否相等(小于,大于等)的具体比较标准。 没有
可比较的
界面。
I'm creating a server application in C++11 using Boost.Asio. I've created a class, Server
, which takes care of accepting new connections. It's basically just:
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();
}
I've found two ways (I'm sure there are more) to "fix" the TODO
comment, i.e. to move the socket to wherever it should go. In my case I just want it back to the class instance that owns the Server
instance (which then wraps it in a Connection
class and inserts it to a list).
Server
has a parameter in its constructor:std::function<void(socket)> OnAccept
which is called inHandleAccept
.- I create an abstract class,
IServerHandler
or whatever, which has one virtual methodOnAccept
.Server
takesIServerHandler
as parameter in its constructor and the class instance owning the server instance extendsIServerHandler
and constructsServer
with*this
as parameter.
What are the pros and cons of option 1 vs option 2? Are there any better options? I'm having the same problem in my Connection
class (OnConnectionClosed
). Also, depending on how I decide to design the system, it might need a OnPacketReceived
and OnPacketSent
callback.
I strongly prefer the first way for several reasons:
Representing concepts/functionality via interfaces/class hierarchies makes the code base less generic, flexible, and then more difficult to mantain or scale in the future. That kind of design imposes a set of requirements on the type (the type implementing the required functionality) which makes it difficult to modify in the future, and most prone to fail when the system changes (Consider what happens when the base class is modified in this type of designs).
What you called the callback approach is just the classic example of duck typing. The server class only expects a callable thing which implements the required functionality, nothing more, nothing less. No "your type must be coupled to this hierarchy" condition is required, so the type which implements handling is completely free.
Also, as I said the server only expects a callable thing: It could be anything with the expected function signature. This gives the user more freedom when implementing a handler. Could be a global function, a bound member function, a functor, etc.
Take the standard library as an example:
Almost all standard library algorithms are based on iterator ranges. There is no
iterator
interface in C++. An iterator is just any type which implements the behaviour of an iterator (Being dereferenceable, comparable, etc). Iterator types are completely free, distinct, and decoupled (Not locked to a given class hierarchy).Another example could be comparators: Whats a comparator? Is just anything with the signature of a boolean comparison function, something callable which takes two parameters and returns a boolean value saying if the two input values are equal (less than, bigger than, etc) from the point of view of a specific comparison criteria. There is no
Comparable
interface.
这篇关于优点& cons的回调(std :: function / std :: bind)vs接口(抽象类)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!