I'm building a C++ application, and I've got several utility objects that all of my classes need to use. These are things like the logging object, the global state object, the DAL object, etc...
Up until this point, I've been passing all of these objects around as references into my class constructors.
For example:
class Honda : public Car { public: Honda ( const GlobalState & state, const Log & logger, const DAL & dal ); ... private: const GlobalState & my_state; const Log & my_logger; const DAL & my_dal; }
This gets tedious fast, because each time I add a utility object that all of my classes need to access, I have to go and change the constructors everywhere.
I've heard that the correct way to solve this problem is to create one struct that contains all the different utility objects and pass that around (as a reference) to all of the objects that need to access it.
Is this the right way to handle this problem? Thanks!
UPDATE: Thank you to everyone for the feedback. After some additional research, I've decided to continue using Dependency Injection.
You can make your GlobalState, Log, DAL classes singletons:
class GlobalState {
public:
static GlobalState& getInstance();
protected:
GlobalState();
GlobalState(const GlobalState&);
GlobalState& operator= (const GlobalState&);
private:
static GlobalState* p_instance;
};
static GlobalState* GlobalState::p_instance = NULL;
/*static*/
GlobalState& getInstance() {
// TODO: acquire lock if multi-threaded
if (!p_instance) { // first time?
p_instance = new GlobalState(); // create sole instance
}
// TODO: release lock if multi-threaded
return *p_instance; // sole instance
}
Then, inside your various methods,
Honda::MyMethod() {
...
const GlobalState& my_state = GlobalState::getInstance();
...
}
You can further simplify your life (and reduce the amount of code duplication) by defining a singleton C++ template, under certain conditions (e.g. all classes' constructors take the same number and types of arguments, etc.)
You could make use of the Service Locator pattern. This article introduces both dependency injection (which you are currently using) and service locator.
However, consider this: the idea of dependency injection is to have a system where each component has a well-defined responsibility and minimizes knowledge of other components where possible. If the component needs other components to do its job, then these are explicitly passed to it. This makes the component simpler to understand, more likely to be correct, and easier to maintain.
If you regularly need to add components which need to be known throughout the system, then there may be something wrong with the design of the system (or the way new features are being added to it). The dependency injection pattern just results in this problem being explicitly visible.
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