Advertisement
Advertisement

More Related Content

Advertisement
Advertisement

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

  1. www.italiancpp.org Going native with less coupling Dependency Injection in C++
  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. 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. Italian C++ Community OO Architecture Benefits i=4 Up Scalability (extension) Down Scalability (contraction) Reusability Safety (testability)
  5. Italian C++ Community i=5 The Wiring Issue In a True Modular Architecture, wiring is critical
  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. Italian C++ Community i=7 Life without a CONTROLLER Example taken from: Carlo Pescio's blog – March 2012
  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. 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. 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. Italian C++ Community Solution #1: Local Creation Each class creates its own dependencies i=11
  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. 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. 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. Italian C++ Community Solution #1: Summary SafeEngine class added SumpPump constructor modified MinePlant modified i=15
  16. Italian C++ Community Solution #2: Factory (not the GoF factory) Create objects without exposing the instantiation logic to the client i=16
  17. Italian C++ Community i=17 <<create>>
  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. Italian C++ Community Solution #2: Summary SafeEngine class added SafeEngineFactory added MinePlant modified i=19
  20. Italian C++ Community Solution #3: Service Locator It’s a registry containing the instances to use i=20
  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. 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. Italian C++ Community Solution #3: Properties Clients aware of the locator Dependencies not explicit / evident Dependencies not checked by compiler i=23
  24. Italian C++ Community Solution #3: Summary SafeEngine class added MinePlant modified i=24
  25. Italian C++ Community Solution #4: Dependency Injection Dependency Injection is when you have something setting the dependencies for you. i=25
  26. Italian C++ Community Solution #4: Dependency Injection Classes don't create their own dependencies They're passed from outside i=26
  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. 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. Italian C++ Community Solution #4: Properties Complete separation between: application logic (classes) wiring (main/builder) i=29
  30. Italian C++ Community Solution #4: Summary SafeEngine class added MinePlant modified (one liner) i=30
  31. Italian C++ Community i=31 Can we do BETTER SafeEngine must be added anyway. … can we remove the one liner in MinePlant? ?
  32. Italian C++ Community i=32 Configuration DrivenWIRING moving creation and wiring outside the code, in a configuration file
  33. Italian C++ Community Why? To easily get extensibility/contraction (without having to touch zillion files and recompile everything) i=33
  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. 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. 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. Italian C++ Community Existing libraries (C++) i=37 QtIOCContainer Sauce DICPP Hypodermic2 Pococapsule Main issues: Compile time injection only Code generators needed
  38. Italian C++ Community Enter Wallaroo Library i=38 wallaroo.googlecode.com wallarooC++ Dependency Injection
  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. 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. Italian C++ Community Object lookup by name i=41 shared_ptr< SumpPump > pump = catalog[ "pump" ];
  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. 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. 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. 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. Italian C++ Community Class Registration i=46 WALLAROO_REGISTER( SumpPump, int ) SumpPump::SumpPump( int id ) : engine( "engine", RegistrationToken() ) { ... } // other methods definition here ...
  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. 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. 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. Italian C++ Community Checks i=50 if ( !catalog.IsWiringOk() ) { // error handling } catalog.CheckWiring() // throws exception
  51. Italian C++ Community Initialization i=51 class Part { ... public: virtual void Init() {} ... }; catalog.Init() // calls Part::Init for each part in catalog
  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. 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. 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. 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. 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. 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. Italian C++ Community Action Points Real OOD (no controllers / managers) Manual Dependency Injection Wallaroo (configuration-driven wiring) i=58
  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
Advertisement