Suppose I have a stream of [acme] objects that I want to expose via an API. I have two choices, callbacks and iterators.
// API #1 // This function takes a user-defined callback // and invokes it for each object in the stream. template<typename CallbackFunctor> void ProcessAcmeStream(CallbackFunctor &callback);
// API #2 // Provides the iterator class AcmeStreamIterator. AcmeStreamIterator my_stream_begin = AcmeStreamIterator::begin(); AcmeStreamIterator my_stream_end = AcmeStreamIterator::end();
API #1 takes the control flow of the program from the user's hand and will not return until the entire stream is consumed (forgetting exceptions for the moment).
API #2 retains the control flow in the user's hand, allowing the user to move forward the stream on his own.
API #1 feels more higher level, allowing the users to jump to the business logic (the callback functor) right away. On the other hand, API #2 feels more flexible, allowing the users lower-level of control.
From a design perspective, which one should I go with? Are there more pros and cons that I have not seen yet? What are some support/maintenance issues down the future?
Some benefits of using IoC. It is easy to switch between different implementations of a particular class at runtime. It increases the modularity of the program. It manages an object's life-cycle and configuration.
You should not use IoC/DI where you cannot demonstrate concrete examples of how the complexity added through the use of IoC/DI is out weighed by the benefits of using IoC/DI.
Primary advantage: encourages attention to the Dependency Inversion Principle, which helps reduce the overall cost of changing the system. Primary disadvantage: highlights design problems and causes many programmers to blame dependency injection for the design problems.
Inversion of Control (IoC) is a design principle that allows classes to be loosely coupled and, therefore, easier to test and maintain. IoC refers to transferring the control of objects and their dependencies from the main program to a container or framework.
The iterator approach is more flexible, with the callback version being easily implemented in terms of the first one by means of existing algorithms:
std::for_each( MyStream::begin(), MyStream::end(), callback );
IMO, the second is clearly superior. While I can (sort of) understand your feeling that it's lower level, I think that's incorrect. The first defines its own specific idea of "higher level" -- but it's one that doesn't fit well with the rest of the C++ standard library, and ends up being relatively difficult to use. In particular, it requires that if the user wants something equivalent to a standard algorithm, it has to be re-implemented from the ground up rather than using existing code.
The second fits perfectly with the rest of the library (assuming you implement your iterators correctly) and gives the user an opportunity for dealing with your data at a much higher level via standard algorithms (and/or new, non-standard algorithms that follow the standard patterns).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With