Pros & cons of a callback (std::function/std::bind) vs an interface (abstract class)

青春壹個敷衍的年華 提交于 2019-11-29 20:09:59
Manu343726

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.

Just to mention that in many cases you do PREFER binding to a specific type.
Therefore in this case declaring that your class MUST have a IServerHandler helps you and other developers understand what interface they should implement in order to work with your class.
In future development when you add more functionality to IServerHandler you force your clients (i.e. derived classes) to keep up with your development.
This might be the desired behavior.

It all boils down to your intentions.

On one hand, If you want to expect that an functionality belongs to a specific type, then it should be implemented in terms of it's hierarchy, such as a virtual function, or a member pointer, etc. Limitation in this sense is good because it helps to makes your code easy to use correctly, and difficult to use incorrectly.

On the other hand, if you just want some abstract "go here and do this" functionality without having to bother with any burden of it being tightly coupled to a specific base class, then clearly something else will be more appropriate like a pointer to a free function, or an std::function, etc.

It's all about which is more fitting to the specific design of any specific part of your software.

What version of boost are you using? The best way IMHO is using coroutines. The code will.be eeasier to.follow. It will look like synchronous code but now I cannot give a comparison since I am writing from a mobile device.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!