13 juillet 2013© Invenietis 2012-2013
Les Concepts
Un de 5 principes SOLID
Dépendances
IoC – Inversion of Control
Objets « Stateful » ou « Stateless »
Cycle de vie
Seam & Composition Root
2
13 juillet 2013© Invenietis 2012-2013
Le discours que vous allez subir…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
3
13 juillet 2013© Invenietis 2012-2013
La conception SOLID
SRP – Single responsibility principle
• Un objet ⇒ Une responsabilité
OCP – Open/Closed principle
• Un composant doit être extensible, pas modifiable.
LSP – Liskov substitution principle
• N’importe quel type compatible doit être substituable.
ISP – Interface segregation principle
• Des interfaces spécialisées plutôt qu’une générale.
DIP – Dependency injection principle
• Hollywood principle: « Don’t call us, we’ll call you »
4
13 juillet 2013© Invenietis 2012-2013
Principe fondamental
- Bénéfices
- Conception clarifiée
- Evolution facilitée
- Réutilisabilité élevée
- Mise en œuvre
- Définir des interfaces (ou des classes abstraites) et les utiliser
- Eviter le « new »
Depend on Abstractions, not Concretions
5
13 juillet 2013© Invenietis 2012-2013
Le discours que vous subissez…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
La résolution de dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
6
13 juillet 2013© Invenietis 2012-2013
Qu’est-ce qu’une dépendance ?
Tout composant, module ou objet « D » qui est extérieur au composant, module ou objet « C » et dont « C » a besoin pour fonctionner.
C’est bien ou c’est mal ?
Ça dépend…
…et des fois, ça dépasse.
7
Objet C(un Contrôleur par exemple)
ConcludeSubscription(){ Cette méthode doit envoyer un mail… }
Objet D(un Service d’envoi de Mail par exemple)
SendMail( recipient, body ){ Cette méthode sait envoyer un mail… }
Dépendance
13 juillet 2013© Invenietis 2012-2013
Le Grand Bazar des Dépendances
• J’utilise « new D() » quand j’en ai besoin.• Je n’utilise pas « new ».
9
13 juillet 2013© Invenietis 2012-2013
Le Grand Bazar des Dépendances
• J’utilise « new D() » quand j’en ai besoin.• Je n’utilise pas « new ».
• J’ai un champ qui contient le D à utiliser.
• Je n’ai pas de champ pour D.
10
13 juillet 2013© Invenietis 2012-2013
Le Grand Bazar des Dépendances
• J’utilise « new D() » quand j’en ai besoin.• Je n’utilise pas « new ».
• J’ai un champ qui contient le D à utiliser.• Ce champ est exposé en tant que Propriété.• Ce champ est configuré via une méthode.• Ce champ ne peut être configuré que depuis le
constructeur.• Je n’ai pas de champ pour D.
11
13 juillet 2013© Invenietis 2012-2013
Le Grand Bazar des Dépendances
• J’utilise « new D() » quand j’en ai besoin.• Je n’utilise pas « new ».
• J’ai un champ qui contient le D à utiliser.• Ce champ est exposé en tant que Propriété.• Ce champ est configuré via une méthode.• Ce champ ne peut être configuré que depuis le
constructeur.• Je n’ai pas de champ pour D.
• Il m’est passé en paramètre de méthode.• Je vais chercher D dans le « Grand Extérieur »
quand j’en ai besoin.
12
13 juillet 2013© Invenietis 2012-2013
Le discours que vous subissez…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
13
13 juillet 2013© Invenietis 2012-2013
Je n’utilise pas « new » : Inversion of Control
Je ne contrôle plus la création de la ressource. Je me contente d’utiliser un objet (idéalement une Abstraction)
Je ne contrôle plus son Cycle de Vie
J’ai abandonné le contrôle…Je viens de vivre une inversion de contrôle.
Ce n’est ni la première, ni la dernière.A chaque IoC, les développeurs souffrent.
14
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
public void Method(){
D dep = ???dep.Run();
}}
15
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
public void Method(){
D dep = new D();dep.Run();
}}
16
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
D _dep;
public void Method(){
_dep.Run();}
}
17
???
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
D _dep;
public D Dep{
get { return _dep; } set { _dep = value; }
}
public void Method(){
_dep.Run();}
}
18
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
D _dep;
public void Init( D dep, int rate )
{ _dep = dep; ...
}
public void Method(){
_dep.Run();}
}
19
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{readonly D _dep;
public C( D dep, int rate ){
_dep = dep; ...
}
public void Method(){_dep.Run();
}}
20
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{public void Method( D dep ){
dep.Run();}
}
21
13 juillet 2013© Invenietis 2012-2013
Concrètement, simplement.
• « new »
• Pas « new »
• Champ pour D
• Propriété
• Méthode
• Constructeur
• Pas de champ pour D
• Paramètre de méthode
• Le « Grand Extérieur »
public class C{
public void Method(){
D dep = ???dep.Run();
}}
22
13 juillet 2013© Invenietis 2012-2013
New is NOT dead! (Faut-il tout “externaliser” ?)
Une Entité est “stateful”
Elle porte de la donnée (qui, souvent, la définit).
Une entité est (souvent) créée lorsque l’on en a besoin via “new”
Un Service est “stateless”
Plusieurs objest (instances) de MailService représentent, de fait, le même service
Les Services sont (très souvent) “injectables”
Les Services dependent des Entités
En théorie, le contraire est faux
23
Ce que j’aime dans le développement, c’est que, parfois, c’est plus subtil que cela, et que ces règles doivent s’accomoder d’exceptions.
13 juillet 2013© Invenietis 2012-2013
Le discours que vous subissez…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
24
13 juillet 2013© Invenietis 2012-2013
Le Grand Extérieur ou « How to not new? »
Ambiant Context
Et son méchant associé le Singleton
Service Provider
Et son collègue le Service Locator
Factory Method
Et son grand frère l’Abstract Factory Method
25
Si l’on injecte pas, il faut aller chercher D “dans le Grand Extérieur”.
13 juillet 2013© Invenietis 2012-2013
Les (très) mauvais Singletons
Très mauvais
Acceptable… Mais uniquement car accepté (opinion personnelle)…
SuperConfig cfg = (SuperConfig)ConfigurationManager.GetSection( "SuperApp" );
public static class Connection{
static SqlConnection _con;
static public SqlConnection Default{
get { return _con ?? (_con = new SqlConnection( "…" ) ); }}
}
static ILogger _log = LogManager.GetLogger<Connection>();
26
13 juillet 2013© Invenietis 2012-2013
Le bon Singleton : Ambiant Context
Un Ambiant Context est une Abstraction
Qui ne fait que fournir de la donnée (lecture seule) ou agir sur une information « totalement globale ».
Cette donnée (très globale) étant susceptible d’intéresser n’importe quel sous-système, n’importe quand.
Un Ambiant Context par excellence
Le TimeProvider
À consommer sans modération en lieu et place de DateTime.UtcNow.
27
13 juillet 2013© Invenietis 2012-2013
The good old Service Provider
Since .Net framework 1.0
Au cœur du Component Model, mais aussi de XAML.
Simple, efficace et souple
si le Service demandé n’existe pas, null est retourné.
namespace System
{
public interface IServiceProvider
{
object GetService( Type serviceType );
}
}
28
13 juillet 2013© Invenietis 2012-2013
Le Service Locator : une aberration « Un Anneau pour les gouverner tous. »
Tentative d’Abstraction de l’IoC, qui réussit le tour de force d’être à la fois « sur et sous-designé »…
ne respecte même pas le contrat du Service Provider (qu’il spécialise).
A oublier!
namespace Microsoft.Practices.ServiceLocation{
public interface IServiceLocator : IServiceProvider{
object GetInstance( Type serviceType );object GetInstance( Type serviceType, string key );IEnumerable<object> GetAllInstances( Type serviceType );TService GetInstance<TService>();TService GetInstance<TService>( string key );IEnumerable<TService> GetAllInstances<TService>();
}}
29
13 juillet 2013© Invenietis 2012-2013
Le Grand Extérieur : L’Usine Center Exemple de Factory Methods (2 méthodes statiques, 2 méthodes d’instances)
En pratique, on appelle une méthode statique:
string fileName = @"C:\Temp\GoogleHomeDownloaded.htm";
WebRequest req = WebRequest.Create( "http://www.google.com" );using( WebResponse resp = req.GetResponse() )using( Stream stream = resp.GetResponseStream() )using( Stream file = File.Create( fileName ) ){
stream.CopyTo( file );}
30
public class C{
public void Method(){
D dep = DFactory.CreateOneD();dep.Run();
}}
13 juillet 2013© Invenietis 2012-2013
Le Grand Extérieur : L’Usine Center Mais?! DFactory est donc un singleton!
Si son fonctionnement doit pouvoir être paramétré, on introduit une Abstract Factory.
Abstract Factory
public class C{
public void Method(){
D dep = theFactory.CreateOneD();dep.Run();
}}
public class C{
public void Method(){
D dep = DFactory.CreateOneD();dep.Run();
}}
Mais ?!theFactory… est une
dépendance à résoudre !
31
13 juillet 2013© Invenietis 2012-2013
Pour le Grand Extérieur, donc…
Ambiant Context
Et son méchant associé le Singleton
Service Provider
Et son collègue le Service Locator
Factory Method
Et son grand frère l’Abstract Factory Method
Les dépendances sont masquées, implicites, invisibles sauf à inspecter l’implémentation.
32
13 juillet 2013© Invenietis 2012-2013
Le discours que vous subissez…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
33
13 juillet 2013© Invenietis 2012-2013
L’Injection des Dépendances (Dependency Injection)
• J’utilise « new D() » quand j’en ai besoin.
• Je n’utilise pas « new ».
• J’ai un champ qui contient le D à utiliser.
• Ce champ est exposé en tant que Propriété.
• Ce champ est configuré via une méthode.
• Ce champ ne peut être configuré que depuis le constructeur.
• Je n’ai pas de champ pour D.
• Il m’est passé en paramètre de méthode.
• Je vais chercher D dans le « Grand Extérieur » quand j’en ai besoin.
ParameterInjection
ContructorInjection
MethodInjection
PropertyInjection
34
13 juillet 2013© Invenietis 2012-2013
« Don’t call us, we’ll call you. »
Notion de « Graphe de Dépendances »
Ces dépendances sont fournies
class C{public C( D dep ) { ... }
}
35
13 juillet 2013© Invenietis 2012-2013
« Don’t call us, we’ll call you. »
Notion de « Graphe de Dépendances »
Ces dépendances sont fournies
class C{public C( D dep ) { ... }
}
class D{public D( E e, G db2 ){ ... }
}
36
13 juillet 2013© Invenietis 2012-2013
« Don’t call us, we’ll call you. »
Notion de « Graphe de Dépendances »
Ces dépendances sont fournies
class C{public C( D dep ) { ... }
}
class D{public D( E e, G db2 ){ ... }
}
class G{public G( string dbName ) { ... }
}
class F{public F( string dbName ) { ... }
}
class E{public E( F db, G db2 ) { ... }
}
37
13 juillet 2013© Invenietis 2012-2013
static C GiveMeC(){var g = new G( "db1" );var f = new F( "db2" );return new C( new D( new E( f, g ), g ) );
}
« Don’t call us, we’ll call you. »
Notion de « Graphe de Dépendances »
Ces dépendances sont fournies
class C{public C( D dep ) { ... }
}
class D{public D( E e, G db2 ){ ... }
}
class G{public G( string dbName ) { ... }
}
class F{public F( string dbName ) { ... }
}
class E{public E( F db, G db2 ) { ... }
}
38
13 juillet 2013© Invenietis 2012-2013
Programming to Abstractions Qui décide des implémentations ?
class C : IC{public C( ID dep ) { ... }
}
39
?
13 juillet 2013© Invenietis 2012-2013
Programming to Abstractions Qui décide des implémentations ?
class C : IC{public C( ID dep ) { ... }
}
class D2 : ID{public D( IE e, IG db2 ){ ... }
}
class G : IG{public G( string d ) { ... }
}
class FGoogle : IF{public FGoogle( int ratio ) { ... }
}
class EV3 : IE{public EV3( IF db, IG db2 ){ ... }
}
40
13 juillet 2013© Invenietis 2012-2013
static IC GiveMeC(){var g = new G( "db1" );var f = new FGoogle( 42 );return new C( new D2( new EV3( f, g ), g ) );
}
Programming to Abstractions Qui décide des implémentations ?
class C : IC{public C( ID dep ) { ... }
}
class D2 : ID{public D( IE e, IG db2 ){ ... }
}
class G : IG{public G( string d ) { ... }
}
class FGoogle : IF{public FGoogle( int ratio ) { ... }
}
class EV3 : IE{public EV3( IF db, IG db2 ){ ... }
}
41
13 juillet 2013© Invenietis 2012-2013
Résoudre les Dépendances
Abstraction Concretion
Gestion des instances : le Cycle de Vie
FGoogle et G sont des « Singletons », C, D et EV3 sont « Transients »
Singleton: toujours la même instance retournée à chaque fois que l’on en a besoin.
Transient: toujours un nouvel objet à chaque fois que l’on en a besoin.
static FGoogle _f;static G _g;
static IC GiveMeC(){
if( _f == null ) _f = new FGoogle( 42 );if( _g == null ) _g = new G( "db1" );return new C( new D( new EV3( _f, _g ), _g ) );
}
42
13 juillet 2013© Invenietis 2012-2013
EV3 n’est plus Transient: il est lié à la requête en cours.
En remplaçant HttpContext.Current.Items par HttpContext.Current.Session, EV3 sera associé à la Session de l’utilisateur.
static FGoogle _f;static G _g;
static IC GiveMeC(){
if( _f == null ) _f = new FGoogle( 42 );if( _g == null ) _g = new G( "db1" );IE e = (IE)HttpContext.Current.Items[typeof(IE)];if( e == null ){
e = new EV3( _f, _g );HttpContext.Current.Items[typeof(IE)] = e;
}return new C( new D( e, _g ) );
}
Entre « Singleton » et « Transient »
43
13 juillet 2013© Invenietis 2012-2013
Le discours que vous subissez…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
44
13 juillet 2013© Invenietis 2012-2013
La règle d’or de la Gestion des Ressources
Ou encore : celui qui obtient un Objet doit prévenir lorsqu’il n’en a plus besoin.
Pas si simple en pratique (voire un peu naïf)
Comment gérer le partage d’objets ?
La nouvelle implémentation d’un objet requiert une destruction alors que ce n’était pas le cas avant ?
C’est un des points clés de la conception à surveiller de près.
Celui qui crée un Objet
est celui qui le détruit.
45
13 juillet 2013© Invenietis 2012-2013
Les « Scopes » de Vie
Une règle simple à respecter (exemple en environnement Web)
C’est, entre autre, ce qu’un Container de DI facilite
Mais en pratique, trop souvent, les Scopes ne s’emboitent pas gentiment comme cela.
Application
Session
Requête
Méthode
X
46
13 juillet 2013© Invenietis 2012-2013
Cycle de Vie et réutilisabilité
A tout Objet concret est associé un Cycle de Vie, ou Style de Vie.
Aux extrêmes, les plus simples :
• Transient
• Singleton
Mais aussi :
• Per Thread
• Per Request
• Per User
• Per Session
• Per Graph
• Per Scope
• …
47
static G _g;…return _g ?? (_g = new G( "db1" ));
return new G( "db1" );
13 juillet 2013© Invenietis 2012-2013
Cycle de Vie et réutilisabilité
A tout Objet concret est associé un Cycle de Vie, ou Style de Vie.
Aux extrêmes, les plus simples :
• Transient
• Singleton
Mais aussi :
• Per Thread
• Per Request
• Per User
• Per Session
• Per Graph
• Per Scope
• …
48
IE e = (IE)HttpContext.Current.Items[typeof(IE)];if( e == null ){e = new EV3( _f, _g );HttpContext.Current.Items[typeof(IE)] = e;
}return e;
static G _g;…return _g ?? (_g = new G( "db1" ));
return new G( "db1" );
var gInGraph = new G( "db1" );return new C( new D( new E( _f, gInGraph ), gInGraph ) );
13 juillet 2013© Invenietis 2012-2013
Allez, c’est bientôt fini…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
49
13 juillet 2013© Invenietis 2012-2013
Les responsabilités d’un Container de DI
Associer une Abstraction à une classe Concrète
Obtenir un Objet
Résoudre ses dépendances
Gérer le Cycle de Vie des Objets
Abstraction
ConcretionInstanciation
Dependencies
50
…Et que ce soit facile à configurer et à utiliser.
…Et si possible, que cela offre d’autres capacités, notamment permettre des approches « Aspect Oriented Programming »
13 juillet 2013© Invenietis 2012-2013
La DI et le .Net framework
Une histoire courte… surtout du point de vue de Microsoft
Spring 1.0 est publié officiellement en mars 2004.
51
From Dependency injection in .Net, Manning 2012
13 juillet 2013© Invenietis 2012-2013
En pratique, 2 exemples
Castle Windsor
Très complet, mature et très bien conçu
Apache License 2.0
Un des seuls à offrir le tracking d’instances (avec AutoFac)
Unity
Issu du groupe Microsoft’s patterns & practices (p&p), supporté par Microsoft
Microsoft Public License (Ms-PL)
Complet, correctement extensible
52
13 juillet 2013© Invenietis 2012-2013
On créé un Container
On le configure (Register)
On l’utilise (Resolve)
On signale la fin de l’utilisation d’un objet (Release)
Principe de fonctionnement
53
Register
Resolve
Release
13 juillet 2013© Invenietis 2012-2013
Principe de fonctionnement On créé un Container
On le configure (Register)
En enregistrant
Des Types concrets
Des associations entre Abstractions (interfaces) et des Types concrets
En précisant
Le Style de Vie
Des configurations de créations
On l’utilise (Resolve)
En résolvant
Des Abstractions
Avec si besoin des paramètres
On signale la fin de l’utilisation d’un objet (Release)
Pour certains Containers et/ou Styles de Vie
54
Register
Resolve
Release
13 juillet 2013© Invenietis 2012-2013
A quoi cela ressemble ? Simple…
55
interface IA{void DoSomething();
}
class A : IA{public void DoSomething(){Console.WriteLine( "A n°{0} do something.", GetHashCode() );
}}
[TestFixture]public class RegisterResolve{[Test]public void RegisterAndResolve(){UnityContainer c = new UnityContainer();// Registerc.RegisterType<IA, A>( new ContainerControlledLifetimeManager() );// Resolvevar a = c.Resolve<IA>();a.DoSomething();
}}
13 juillet 2013© Invenietis 2012-2013
Tout ça pour ça ?
Une énorme Factory éminemment configurable ?
• La configuration est centralisée
• Elle peut s’exprimer en code et/ou en ressources (typiquement en Xml)
• Les dépendances sont gérées automatiquement, quelque soit leurs niveaux
Un super ServiceProvider ?
• Les implémentations sont masquées, il devient facile de travailler avec des Abstractions
• Le cycle de vie des objets est géré par le Container, je ne m’en occupe plus
56
Oui ! Mais…
13 juillet 2013© Invenietis 2012-2013
Allez, c’est bientôt fini…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
57
13 juillet 2013© Invenietis 2012-2013
Bien utiliser la DI != Savoir utiliser un Container
Trop souvent sur- ou mal- utilisé
Dependency Injection != Utiliser un Container
58
Outillage
ConceptionPas de Container de DI Container de DI
Pas de DICouplage fort, problématiquestechniques (constructions, cycle de vie) intriquées avec le Métier.
Service Locator Anti-Pattern. Dépendances invisibles, difficile à maintenir.
DICodage à la main de l’obtention/instanciation des dépendances.
De nombreux bénéfices associés.
13 juillet 2013© Invenietis 2012-2013
Bien utiliser la DI != Savoir utiliser un Container
Trop souvent sur- ou mal- utilisé
Un Container de DI n’est qu’un facilitateur
Une bonne architecture n’exige pas de Container
On peut toujours décider de ne pas utiliser de Container
Avant de l’utiliser, il y a deux notions clés à comprendre
SEAM : la soudure, le joint
COMPOSITION ROOT : le support principal de l’injection
59
Before practicing Zen, mountains were mountains and rivers were rivers.
While practicing Zen, mountains are no longer mountains and rivers are no longer rivers.
After realization, mountains are mountains and rivers are rivers again.
13 juillet 2013© Invenietis 2012-2013
SEAM et COMPOSITION ROOT (Démo)
Un framework (très) simple
Qui peut fonctionner sans DI
C’est l’occasion de « voir » la DI à l’œuvre
Et aussi d’apprendre, un peu, à s’en servir
60
Tout framework bien conçu expose un ou quelques SEAM. Ces « soudures » sont des points centraux du framework qui instancient des COMPOSITION ROOT qui sont les objets en charge de la majorité des opérations.
Dans le cadre de MVC, les objets Contrôleurs sont les principaux COMPOSITION ROOT, et le ControllerFactory le principal SEAM à considérer.
13 juillet 2013© Invenietis 2012-2013
Chercher le SEAM…
…y introduire l’unique appel au Container de DI
…pour obtenir la racine de composition
Toutes les dépendances de la racine de composition sont résolues…
…la racine est opérationnelle
…le système fonctionne.
61
Mise en œuvre de la DI
C’est souvent aussi simple que cela.
Mais, malheureusement, pas toujours…
13 juillet 2013© Invenietis 2012-2013
Dernière ligne droite…
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
62
13 juillet 2013© Invenietis 2012-2013
Lorsque la résolution doit être différée…
Parce que la dépendance a solliciter dépend du contexte d’exécution
Multi-tenancy
Privilèges, niveaux d’accès
On ne peut donc pas l’injecter systématiquement
Parce que la dépendance est « optionnelle »
Elle ne sera sollicitée, statistiquement, que dans quelques cas
Elle est coûteuse
on ne souhaite donc pas l’injecter systématiquement
63
Et il ne peut y avoir que deux raisons à cela !
13 juillet 2013© Invenietis 2012-2013
Une Solution, plusieurs mises en œuvre
Introduire une indirection sous la forme d’une Factory
La racine de composition dépend de la Factory
La Factory est sollicitée lorsque la dépendance est nécessaire
Techniquement, la Factory peut-être :
Une simple Func<T> qui rappellera le Container
Cette fonction peut comporter des paramètres supplémentaires
Une interface dédiée
Avec une ou plusieurs méthodes qui renvoient la dépendance…
…et des méthodes qui permettent de signaler la fin de l’utilisation (Release)
64
Factory
13 juillet 2013© Invenietis 2012-2013
Un problème… de dépendances !
La Factory est liée au type du Container utilisé dans l’Application
Cela couple le « Métier » à « l’Infrastructure »
C’est mal!
Découpler ?
Func<T> est supporté par la quasi-totalité des Containers
La meilleure solution est définitivement (de mon point de vue) celle de Castle.Windsor
La « Typed Factory » est une interface qui est automagiquement implémentée.
http://devlicio.us/blogs/krzysztof_kozmic/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx
65
13 juillet 2013© Invenietis 2012-2013
Enfin !
Programmation SOLID
Qu’est-ce qu’une dépendance ?
Qu’est-ce que l’Inversion de Contrôle ?
Si on est pas à Hollywood, on est dans le « Grand Extérieur »
L’Injection des Dépendances
Les « styles de vies » ou « combien de temps me reste-t-il à vivre? »
Les containers de DI
La bonne Dependency Injection
L’enjeu de la « résolution tardive »
Pièges et Conclusion
66
13 juillet 2013© Invenietis 2012-2013
Quelques pièges de la DI
Les Containers ne sont pas identiques… loin s’en faut
Même les basiques divergent
Certains exigent un enregistrement explicite…
…d’autres décident d’instancier ce qu’ils veulent (c’est le cas de Unity)
Les enregistrements multiples, les enregistrements nommés
Le choix du constructeur quand il y en a plusieurs (faut-il prendre le plus long ou le plus court ?)
Opt-in ou Opt-out pour les propriétés ?
Les dépendances circulaires
Le contrôle du cycle de vie
Voir l’annexe qui suit pour les 2 approches.
67
UnityContainer c = new UnityContainer();var r = c.Resolve<Random>();
13 juillet 2013© Invenietis 2012-2013
Extensibilité des Containers
Unity, Castle, AutoFac, etc… sont réellement extensibles
Exemple de Unity
Corriger le comportent par défaut
Autres exemples
Gérer un fallback déterministe sur les enregistrements nommés
Introduire un mapping automatique de certaines interfaces (« Ambiant Contract »)
68
13 juillet 2013© Invenietis 2012-2013
Conclusion
Concevoir indépendamment d’un Container
Comprendre les enjeux de la Conception logicielle
Viser SOLID
Utiliser les Containers comme des Facilitateurs
Ne pas dépendre de leur spécificités
Rester dans les clous
Connaître son Container
Et ne pas hésiter à l’étendre si besoin est
69
13 juillet 2013© Invenietis 2012-2013
Bibliographie “Dependency Injection in .Net’, Mark Seeman, Manning 2012
http://www.martinfowler.com/articles/injection.htmlUn must-read…
http://mikehadlow.blogspot.com/2011/02/mvc-30-idependencyresolver-interface-is.htmlhttp://kozmic.pl/2010/08/19/must-windsor-track-my-components/ Les tenants du « Le Container doit gérer totalement le cycle de vie » : ce sont principalement des membres de la communauté Windsor
http://davybrion.com/blog/2010/02/avoiding-memory-leaks-with-nservicebus-and-your-own-castle-windsor-instance/NServiceBus s’adapte à Castle… Mais la conclusion est que NSB doit, un jour, explicitement offrir un point de Release dans le cadre de son fonctionnement.
http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/
Excellent article par le créateur de Autofac qui explique la (très bonne) gestion du Lifetime dans Autofac.
http://nblumhardt.com/2010/01/the-relationship-zoo/Toujours un excellent article: une typologie des relations entre les objets.
http://www.joelonsoftware.com/articles/LeakyAbstractions.htmlJoel Spolsky : « All non-trivial abstractions, to some degree, are leaky. »
http://giorgiosironi.blogspot.com/2009/07/when-to-inject-distinction-between.htmlInjecter ou créer ?
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/Démontre le danger des singletons « cachés ». Peut être lue de la même façon comme un argument contre le ServiceLocator. Cela dit, à mon avis, le problème de fond est dans le fait d’utiliser des classes concrètes (CreditCard) au lieu d’abstractions (ICreditCard) qui sont de facto décorrélés de leurs implémentations.
70
13 juillet 2013© Invenietis 2012-2013
Cycle de Vie: les deux approches
Le Container traque les instances (et leurs dépendances)
Il faut appeler une méthode explicite Release( o ) lorsque l’on ne se sert plus de o
C’est le mode « Rolls-Royce tout terrain »
• Indépendant du type d’application
• Si les développeurs ET l’infrastructure appellent correctement Release( o )
OU (souvent exclusif)
L’application gère ses « Scopes »
Moins de bookkeeping (donc d’overhead)
Particulièrement adapté au modèle Request/Response
Sensiblement couplé à un type d’application, un Host particulier
Lecture fortement recommandée:
http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/
71
Top Related