Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Going native with less coupling: Dependency Injection in C++

Slideshow from C++ Meetup Bologna 2014, about the central role of Dependency Injection in OO software.
The slide deck contains detailed explanation about dependency injection in general and C++ frameworks in particular.

Going native with less coupling: Dependency Injection in C++

  1. 1. www.italiancpp.org Going native with less coupling Dependency Injection in C++
  2. 2. Italian C++ Community Style 1: [Fake] OO Design i=2 CONTROLLER / MANAGER Dumb object Dumb object Dumb object Dumb object Dumb object Dumb object Dumb object Dumb object
  3. 3. Italian C++ Community Style 2: [True] OO design i=3 Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object Smart Object
  4. 4. Italian C++ Community OO Architecture Benefits i=4 Up Scalability (extension) Down Scalability (contraction) Reusability Safety (testability)
  5. 5. Italian C++ Community i=5 The Wiring Issue In a True Modular Architecture, wiring is critical
  6. 6. Italian C++ Community The Wiring Issue When you've got a Good Architecture, you deal with requirements changes by adding/removing/substituting objects So, we need a simple way to: Change the type of the objects Create new objects Modify the wiring of the objects i=6
  7. 7. Italian C++ Community i=7 Life without a CONTROLLER Example taken from: Carlo Pescio's blog – March 2012
  8. 8. Italian C++ Community First Design i=8 MinePlant SumpPump +Drain() PumpEngine +On() +Off() DigitalOutput +Write() GasSensor <<interface>> +IsCritical() GasAlarm +Watch() 1..* Alarm +On() +Off() SafeEngine
  9. 9. Italian C++ Community Requirements Change i=9 MinePlant SumpPump +Drain() PumpEngine +On() +Off() DigitalOutput +Write() GasSensor <<interface>> +IsCritical() GasAlarm +Watch() 1..* Alarm +On() +Off() SafeEngine
  10. 10. Italian C++ Community Extension The design is robust: I only need to add a class But… Who creates SafeEngine instead of PumpEngine? How does SafeEngine get the pointer to the GasSensor (the same used by GasAlarm)? i=10
  11. 11. Italian C++ Community Solution #1: Local Creation Each class creates its own dependencies i=11
  12. 12. Italian C++ Community i=12 MinePlant SumpPump +Drain() PumpEngine +On() +Off() DigitalOutput +Write() GasSensor <<interface>> +IsCritical() GasAlarm +Watch() 1..* Alarm +On() +Off() SafeEngine <<create>>
  13. 13. Italian C++ Community Solution #1: Consequences The SumpPump constructor creates a SafeEngine instead of a PumpEngine. … but SafeEngine needs a pointer to the GasSensor instance already used by GasAlarm. So, we must pass it as a parameter to SumpPump constructor. i=13
  14. 14. Italian C++ Community Solution #1: Properties If I need to change the concrete type, I have to modify the client. It's difficult to reuse the same client class (even in the same application). i=14
  15. 15. Italian C++ Community Solution #1: Summary SafeEngine class added SumpPump constructor modified MinePlant modified i=15
  16. 16. Italian C++ Community Solution #2: Factory (not the GoF factory) Create objects without exposing the instantiation logic to the client i=16
  17. 17. Italian C++ Community i=17 <<create>>
  18. 18. Italian C++ Community Solution #2: Consequences The SumpPump constructor takes a Factory as parameter. PumpEngineFactory instantiates a PumpEngine. SafeEngineFactory instantiates a SafeEngine. SafeEngine still needs a pointer to the GasSensor instance already used by GasAlarm, so we must pass it to the SafeEngineFactory constructor. i=18
  19. 19. Italian C++ Community Solution #2: Summary SafeEngine class added SafeEngineFactory added MinePlant modified i=19
  20. 20. Italian C++ Community Solution #3: Service Locator It’s a registry containing the instances to use i=20
  21. 21. Italian C++ Community Solution #3: Service Locator i=21 class ServiceLocator { public: static ServiceLocator& Instance(); shared_ptr< PumpEngine > Engine(); void Engine( const shared_ptr< PumpEngine >& engine ); private: ... };
  22. 22. Italian C++ Community Solution #3: Service Locator i=22 // MinePlant: auto e = make_shared< SafeEngine >( engineOutput, gasSensor ); ServiceLocator::Instance().Engine( e ); // SumpPump: SumpPump::SumpPump() : engine( ServiceLocator::Instance().Engine() ) { }
  23. 23. Italian C++ Community Solution #3: Properties Clients aware of the locator Dependencies not explicit / evident Dependencies not checked by compiler i=23
  24. 24. Italian C++ Community Solution #3: Summary SafeEngine class added MinePlant modified i=24
  25. 25. Italian C++ Community Solution #4: Dependency Injection Dependency Injection is when you have something setting the dependencies for you. i=25
  26. 26. Italian C++ Community Solution #4: Dependency Injection Classes don't create their own dependencies They're passed from outside i=26
  27. 27. Italian C++ Community Dependency Injection i=27 auto gasSensor = ... auto alarmOutput = make_shared<DigitalOutput>("/dev/ttyS0"); auto alarm = make_shared<Alarm>(alarmOutput); auto gasAlarm = make_shared<GasAlarm>(gasSensor,alarm); auto engineOutput = make_shared<DigitalOutput>("/dev/ttyS1"); auto engine = make_shared<PumpEngine>(engineOutput); auto pump = make_shared<SumpPump>(engine);
  28. 28. Italian C++ Community Dependency Injection i=28 auto gasSensor = ... auto alarmOutput = make_shared<DigitalOutput>("/dev/ttyS0"); auto alarm = make_shared<Alarm>(alarmOutput); auto gasAlarm = make_shared<GasAlarm>(gasSensor,alarm); auto engineOutput = make_shared<DigitalOutput>("/dev/ttyS1"); // auto engine = make_shared<PumpEngine>(engineOutput); auto engine = make_shared<SafeEngine>(engineOutput,gasSensor); auto pump = make_shared<SumpPump>(engine);
  29. 29. Italian C++ Community Solution #4: Properties Complete separation between: application logic (classes) wiring (main/builder) i=29
  30. 30. Italian C++ Community Solution #4: Summary SafeEngine class added MinePlant modified (one liner) i=30
  31. 31. Italian C++ Community i=31 Can we do BETTER SafeEngine must be added anyway. … can we remove the one liner in MinePlant? ?
  32. 32. Italian C++ Community i=32 Configuration DrivenWIRING moving creation and wiring outside the code, in a configuration file
  33. 33. Italian C++ Community Why? To easily get extensibility/contraction (without having to touch zillion files and recompile everything) i=33
  34. 34. Italian C++ Community From Identifiers to Strings Improving Previous Solution: Objects creation from string Objects identified by name Objects connected by name i=34
  35. 35. Italian C++ Community Run-time Reflection Missing… Create("Foo") vs new Foo Enumerate the dependencies “Inject” the right object address in a class dependency i=35
  36. 36. Italian C++ Community Solution #5: Dependency Injection + Dependency Injection is when you have something setting the dependencies for you. …this something is usually a framework. i=36
  37. 37. Italian C++ Community Existing libraries (C++) i=37 QtIOCContainer Sauce DICPP Hypodermic2 Pococapsule Main issues: Compile time injection only Code generators needed
  38. 38. Italian C++ Community Enter Wallaroo Library i=38 wallaroo.googlecode.com wallarooC++ Dependency Injection
  39. 39. Italian C++ Community Creating objects i=39 Catalog catalog; ... catalog.Create("alarmOutput","DigitalOutput","/dev/ttyS0"); catalog.Create("alarm","Alarm"); catalog.Create("gasAlarm","GasAlarm"); catalog.Create("engineOutput","DigitalOutput","/dev/ttyS1"); catalog.Create("pump","SumpPump"); catalog.Create("engine","SafeEngine");
  40. 40. Italian C++ Community Creating objects (from cfg) i=40 <parts> <part> <name>pump</name> <class>SumpPump</class> </part> <part> <name>engine</name> <class>SafeEngine</class> </part> </parts> ... Catalog catalog; XmlConfiguration file("wiring.xml"); file.Fill( catalog ); ...
  41. 41. Italian C++ Community Object lookup by name i=41 shared_ptr< SumpPump > pump = catalog[ "pump" ];
  42. 42. Italian C++ Community Connect Things by name (DSL) i=42 Catalog catalog; // fill catalog ... use(catalog["alarmOutput"]).as("out").of(catalog["alarm" ]); use(catalog["safeEngine"]).as("engine").of(catalog["pump"]);
  43. 43. Italian C++ Community Connect Things by name (DSL) i=43 Catalog catalog; // fill catalog ... wallaroo_within( catalog ) { use( "alarmOutput" ).as( "out" ).of( "alarm" ); use( "safeEngine" ).as( "engine" ).of( "pump" ); }
  44. 44. Italian C++ Community Connect Things by name (from cfg) i=44 <wiring> <wire> <source>alarm</source> <dest>alarmOutput</dest> <collaborator>out</collaborator> </wire> <wire> <source>pump</source> <dest>safeEngine</dest> <collaborator>engine</collaborator> </wire> </wiring> Catalog catalog; ... XmlConfiguration file( "wiring.xml" ); file.Fill( catalog ); catalog.CheckWiring(); ...
  45. 45. Italian C++ Community Class Declaration i=45 #include "wallaroo/registered.h" using namespace wallaroo; class SumpPump : public Part { public: SumpPump( int id ); private: Collaborator< Engine > engine; };
  46. 46. Italian C++ Community Class Registration i=46 WALLAROO_REGISTER( SumpPump, int ) SumpPump::SumpPump( int id ) : engine( "engine", RegistrationToken() ) { ... } // other methods definition here ...
  47. 47. Italian C++ Community Shared Libraries – AKA plugins (code) i=47 Plugin::Load( "safeengine" + Plugin::Suffix() ); // Plugin::Suffix() expands to .dll or .so according to the OS
  48. 48. Italian C++ Community Shared Libraries – AKA plugins (cfg) i=48 <plugins> <shared>safeengine</shared> </plugins> Catalog catalog; XmlConfiguration file( "wiring.xml" ); // load the shared libraries specified in the configuration file: file.LoadPlugins(); file.Fill( catalog ); // throws a WiringError exception if any dependency is missing: catalog.CheckWiring();
  49. 49. Italian C++ Community Collections i=49 class Car : public wallaroo::Part { ... private: Collaborator< Engine > engine; Collaborator< AirConditioning, optional > airConditioning; Collaborator< Airbag, collection > airbags; Collaborator< Speaker, collection, std::list > speakers; Collaborator< Seat, bounded_collection< 2, 6 > > seats; };
  50. 50. Italian C++ Community Checks i=50 if ( !catalog.IsWiringOk() ) { // error handling } catalog.CheckWiring() // throws exception
  51. 51. Italian C++ Community Initialization i=51 class Part { ... public: virtual void Init() {} ... }; catalog.Init() // calls Part::Init for each part in catalog
  52. 52. Italian C++ Community Wallaroo Internals WALLAROO_REGISTER declares a static object. Its constructor creates a factory and puts it in a table, with the class name as key. Catalog::Create uses the factory to put a new instance in the catalog. wallaroo::Part has a table of <name, Collaborator> i=52
  53. 53. Italian C++ Community Wallaroo Internals shared_ptr< Foo > foo = catalog[ “foo” ]; catalog[ “foo” ] returns a class that defines operator shared_ptr< T >() Collaborator uses weak_ptr for the dependency Collaborator defines operator shared_ptr() and operator->() i=53
  54. 54. Italian C++ Community Wallaroo Internals i=54 wallaroo_within( catalog ) { use("alarmOutput").as("out").of("alarm"); use("safeEngine").as("engine").of("pump"); } for ( Context c(catalog);c.FirstTime();c.Terminate() ) { use("alarmOutput").as("out").of("alarm"); use("safeEngine").as("engine").of("pump"); }
  55. 55. Italian C++ Community Wallaroo Strengths Lightweight (header file only) Portable Type Safe DSL syntax for object creation and wiring Configuration driven wiring (xml and json) Shared library support (plugin) C++11/14 or boost interface No code generators i=55
  56. 56. Italian C++ Community Design is a balance of forces Intrusive VS Non Intrusive Non Intrusive Solutions can manage existing classes but require code generators for configuration driven wiring i=56
  57. 57. Italian C++ Community Design is a balance of forces Configuration-driven wiring VS static type checking By moving the wiring in a configuration file, we give up the static type checking. But it’s ok, since you build your system at startup. i=57
  58. 58. Italian C++ Community Action Points Real OOD (no controllers / managers) Manual Dependency Injection Wallaroo (configuration-driven wiring) i=58
  59. 59. Italian C++ Community References & Credits Me: @DPallastrelli Me: it.linkedin.com/in/pallad Rate me: https://joind.in/12277 Wallaroo: wallaroo.googlecode.com i=59 MinePlant example from Carlo Pescio's blog (http://www.carlopescio.com/2012/03/life- without-controller-case-1.html) Wiring Picture: By Gael Mace (Own work (Personal photograph)) [CC-BY-3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons

×