illustrates the anti-pattern:
struct NetworkSystem
{
NetworkSystem(Gx::Context& ioc) // DON'T DO THIS. Stick with the example I provided above
{
config = ioc.Require<Config>();
logger = ioc.Require<Logger>();
timer = ioc.Require<Timer>();
profiler = ioc.Require<Profiler>();
}
Config* config; Logger* logger; Timer* timer; Profiler *profiler;
};
auto ioc = Gx::Context();
auto networkSystem = NetworkSystem(ioc); // just don't
The above case is an anti-pattern because **it hides dependencies.** When a class receives the entire container, its constructor signature no longer tells you what it actually needs, which defeats the purpose of [DI](https://en.wikipedia.org/wiki/Dependency_injection). IoC container should be primarily used in the root composition of your classes' initialization (e.g, your `main()`).
In addition, many IoC containers perform compile-time checks to some extent regardless of the language. By passing the container directly, you are giving up compile-time checks that the library can otherwise perform (e.g., `ioc.Require<NetworkSystem>()` may fail at compile-time if one of the dependencies is not constructible either by the library (multiple ambiguous constructors) or by the nature of the class itself). I think we all could agree that we should enforce compile-time checks whenever possible.
Just like other programming patterns, some exceptions may apply, and it might be more practical to go with anti-pattern in some particular situations (that's why `Require<T>` in my lib is exposed anyway, it could be used for different purposes).
There might be other anti-patterns I couldn't remember off the top of my head, but the above is the most common mistake. There are a bunch of resources online that discuss this.
This is a pretty common concept for web dev folk (and maybe gamedev?), but I guess it is not for your typical C++ dev
https://redd.it/1ro288e
@r_cpp
Post #24826
15