59
www.italiancpp.org Going native with less coupling Dependency Injection in C++

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

Embed Size (px)

DESCRIPTION

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.

Citation preview

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

www.italiancpp.org

Going native with less couplingDependency Injection in C++

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

Italian C++ Community

Style 1: [Fake] OO Design

i=2

CONTROLLER /MANAGER

Dumbobject

Dumbobject

Dumbobject

Dumbobject

Dumbobject

Dumbobject

Dumbobject

Dumbobject

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

Italian C++ Community

Style 2: [True] OO design

i=3

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

SmartObject

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

Italian C++ Community

OO Architecture Benefits

i=4

Up Scalability (extension)

Down Scalability (contraction)

Reusability

Safety (testability)

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

Italian C++ Community i=5

The Wiring

Issue

In a True Modular Architecture, wiring is critical

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

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

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

Italian C++ Community i=7

Life without a

CONTROLLER

Example taken from:Carlo Pescio's blog – March 2012

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

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

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

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

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

Italian C++ Community

ExtensionThe 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

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

Italian C++ Community

Solution #1: Local Creation

Each class creates its own dependencies

i=11

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

Italian C++ Community i=12

MinePlant

SumpPump

+Drain()

PumpEngine

+On()+Off()

DigitalOutput

+Write()

GasSensor<<interface>>

+IsCritical()

GasAlarm

+Watch()

1..*

Alarm

+On()+Off()

SafeEngine <<create>>

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

Italian C++ Community

Solution #1: Consequences

The SumpPump constructor creates a SafeEngineinstead of a PumpEngine.

… but SafeEngine needs a pointer to the GasSensorinstance already used by GasAlarm.

So, we must pass it as a parameter to SumpPumpconstructor.

i=13

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

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

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

Italian C++ Community

Solution #1: Summary

SafeEngine class addedSumpPump constructor modifiedMinePlant modified

i=15

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

Italian C++ Community

Solution #2: Factory

(not the GoF factory)

Create objects without exposing the instantiation logic to the client

i=16

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

Italian C++ Community i=17

<<create>>

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

Italian C++ Community

Solution #2: ConsequencesThe SumpPump constructor takes a Factory as parameter.

PumpEngineFactory instantiates a PumpEngine.

SafeEngineFactory instantiates a SafeEngine.

SafeEngine still needs a pointer to the GasSensorinstance already used by GasAlarm, so we must pass it to the SafeEngineFactory constructor.

i=18

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

Italian C++ Community

Solution #2: Summary

SafeEngine class addedSafeEngineFactory addedMinePlant modified

i=19

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

Italian C++ Community

Solution #3: Service Locator

It’s a registry containing the instances to use

i=20

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

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:...

};

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

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() )

{ }

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

Italian C++ Community

Solution #3: Properties

Clients aware of the locator

Dependencies not explicit / evident

Dependencies not checked by compiler

i=23

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

Italian C++ Community

Solution #3: Summary

SafeEngine class added

MinePlant modified

i=24

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

Italian C++ Community

Solution #4: Dependency Injection

Dependency Injection is when you have something setting the dependencies for you.

i=25

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

Italian C++ Community

Solution #4: Dependency Injection

Classes don't create their own dependencies

They're passed from outside

i=26

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

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);

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

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);

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

Italian C++ Community

Solution #4: Properties

Complete separation between:

application logic (classes)

wiring (main/builder)

i=29

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

Italian C++ Community

Solution #4: Summary

SafeEngine class addedMinePlant modified (one liner)

i=30

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

Italian C++ Community i=31

Can we do

BETTERSafeEngine must be added anyway.

… can we remove the one liner in MinePlant?

?

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

Italian C++ Community i=32

Configuration

DrivenWIRINGmoving creation and wiring outside the code,

in a configuration file

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

Italian C++ Community

Why?

To easily get extensibility/contraction

(without having to touch zillion files and recompile everything)

i=33

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

Italian C++ Community

From Identifiers to Strings

Improving Previous Solution:

Objects creation from string

Objects identified by name

Objects connected by name

i=34

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

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

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

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

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

Italian C++ Community

Existing libraries (C++)

i=37

QtIOCContainer

Sauce

DICPP

Hypodermic2

Pococapsule

Main issues:Compile time injection only

Code generators needed

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

Italian C++ Community

Enter Wallaroo Library

i=38

wallaroo.googlecode.com

wallarooC++ Dependency Injection

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

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");

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

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 );...

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

Italian C++ Community

Object lookup by name

i=41

shared_ptr< SumpPump > pump = catalog[ "pump" ];

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

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"]);

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

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" );

}

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

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();...

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

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;};

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

Italian C++ Community

Class Registration

i=46

WALLAROO_REGISTER( SumpPump, int )

SumpPump::SumpPump( int id ) :engine( "engine", RegistrationToken() )

{...}

// other methods definition here...

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

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

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

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();

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

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;

};

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

Italian C++ Community

Checks

i=50

if ( !catalog.IsWiringOk() ){

// error handling}

catalog.CheckWiring() // throws exception

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

Italian C++ Community

Initialization

i=51

class Part{...public:

virtual void Init() {}...};

catalog.Init() // calls Part::Init for each part in catalog

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

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

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

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

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

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");

}

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

Italian C++ Community

Wallaroo StrengthsLightweight (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

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

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

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

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

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

Italian C++ Community

Action Points

Real OOD (no controllers / managers)

Manual Dependency Injection

Wallaroo (configuration-driven wiring)

i=58

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

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