On Sun, 21 Feb 2021 at 00:27, Krzysztof Jusiak via Boost < boost@lists.boost.org> wrote:
Let me try to explain DI (manual and automatic) via example.
DI vs NO-DI is all about less coupling by injecting dependencies instead of. It's often referred as a Hollywood prince - Don't call us we will call you!
Example
struct coupled_no_di { void api() { d.call(); } depedency d{singleton::get()}; };
- Coupled design (hard to test)
struct not_coupled_di { not_coupled_di(dependency& d) : d_{d} {}
void api() { d.call(); } depedency& d; };
- Much better - separation of business logic and object creation.
But we can do even better by applying - Dependency Inversion (relay on abstractions and not concrete implementations) - Abstractions can be done in many different ways (type erasure, templates, abstract classes, etc.)
template<DependencyConcept TDependency> struct not_coupled_di { not_coupled_di(TDependency& d) : d_{d} {}
void api() { d.call(); } TDependency& d; };
The above is much better because - It's not coupled to any specific implementation - Can be changed for testing (mocks/fakes/stubs)
Okay, now let's take at the main. A good design will apply a Composition Root (unique place in the app when dependencies are being created)
int main() { my_depedency dependency{...}; not_coupled_di di{dependency};
di.api(); }
- The above is an example of manual DI which is cool already but may lead to what is called a Wiring Mess.
Let's imagine that we have a big dependency tree because we are following SOLID principle and we especially apply the Single Responsibility Principle (we will have a lot of dependencies).
int main() { my_dependency_1 d1{}; my_dependency_2 d2{}; my_dependency_3 d2{d1, d2}; my_dependency_4 d3{d1, 42, d3}; app a{d3, ...}; ...
// Order of the above is important and with bigger projects might be easily 10k LOC+ }
- Well, we can maintain the above if we want, but Automatic DI will let us actually focus on the business logic instead! - Any change in the constructors (a reference to shared_pointer, the order of parameters) will require us to change the above :(
Boost.DI library can help with removing the Wiring Mess for us, though!
int main() { auto injector = make_injector(); app = create<app>(injector); return app.api(); }
Right now the benefits of using the framework instead of manual DI - If we change any constructor parameter or order of any dependency we DON't have to change anything with DI framework
- Before not_coupled_di(TDependency& d);
- Refactor not_coupled_di(std::shared_ptr<TDependency>);
With manual DI the wiring has to be changed to pass the shared_ptr with Boost.DI we don't have to change the wiring code at all.
Okay, but what about the polymorphism behaviour. Boost.DI allows a different type of polymorphism. One can inject templates, abstract classes, variant, type erasure etc. More can be found here - https://github.com/boost-ext/di/tree/cpp14/example/polymorphism.
Why that's important? Because it's a better design and makes testing easier too. How to do it with Boost.DI?
// configuration auto module = make_injector( bind<interface>.to<implementation> // I'm not going to go into details, but Boost.DI allows to inject TEMPLATES, abstract classes, etc... );
// production, release int main() { app = create<app>(module); return app.api(); }
// end 2 end testing int main() { auto injector = di::make_injector( module(), // production wiring di::bind<interface>.to<mock> [override] ); app = create<app>(module); return app.api(); }
- Great, we can test end 2 end with overriding some dependencies such as time, database, networking, logging etc... - We have a loosely coupled design! - We don't have to change ANYTHING in the wiring code when we change the dependency tree and/or any constructor parameters/order
I hope that helps a bit?
Extremely helpful, thank you. So in summary it’s a clever bag of arguments that are matched by type to any constructor it’s applied to. The motivating use case is to reduce the burden of maintaining the dependencies of application objects. Is that a succinct, if simplistic, summary?
I also really encourage to take a quick look at which summaries concepts behind DI. - https://www.youtube.com/watch?v=yVogS4NbL6U
Thanks, Kris
On Sat, Feb 20, 2021 at 4:10 PM Peter Dimov via Boost < boost@lists.boost.org> wrote:
Andrzej Krzemienski wrote: ...
So, I want to apply the Dependency Injection philosophy. I get:
class State { Rect area; Point location; // invariant: location is inside area; public: State(Rect a, Point loc) : area{a}, location{loc} {} };
int main() { State s{Rect{{0, 0}, {2, 2}}, Point{1, 1}}; } ```
Now, I choose to use the DI-library (this is where I have troubles with understanding: why would I want to do that?).
You are thinking in C++ (value semantics) but you need to be thinking in Java (reference semantics, OOP, object DAGs.) Suppose you need to have a logger object
shared_ptr<ILogger> pl; // new FileLogger( log_file_name fn );
and a database connector object (which uses a logger to log things)
shared_ptr<IDbConn> pdb; // new MysqlDb( mysql_db_info mdi, shared_ptr<ILogger> pl )
and an abstract configuration query interface, that can use an .ini file, or a JSON file, or a database
shared_ptr<IConfig> pc; // new DbConfig( shared_ptr<IDbConn> pdb, shared_ptr<ILogger> pl );
Now when you need to create the config query object, you need to give it a database connector and a logger, and you also need to give the same logger to the database connector. So with [NotYetBoost].DI you'll write something like
auto injector = make_injector( bind
.to( "something.log" ), bind<ILogger>.to<FileLogger>(), bind<>.to( mysql_db_info{ ... } ), bind<IDbConn>.to<MysqlDb>(), bind<IConfig>.to<DbConfig>() ); auto cfg = injector.create<IConfig>();
although I don't really know if this will compile or work. :-)
Now imagine that same thing but with 10 objects, or 20 objects. Also imagine that your day to day vocabulary includes "business logic", "enterprise", "UML" and "XML".
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212