L’injection de dépendances est souvent sous-estimée, mais cruciale pour la modularité du code.
Sans elle, le code devient difficile à maintenir et à tester, conduisant à des erreurs coûteuses.
Découvrez comment Dagger simplifie ce processus, rendant votre code plus propre et performant.
Apprenez à construire des applications Android avec une architecture moderne et performante!
Nous allons explorer Dagger , un framework populaire pour l’injection de dépendances en Java et Kotlin. L’injection de dépendances est une technique essentielle en développement logiciel qui permet de rendre le code plus modulaire, testable et maintenable. Nous discuterons des concepts de base, des annotations couramment utilisées, et de la façon dont Dagger gère les scopes pour optimiser l’instantiation des objets.
Testabilité et modularité du code
L’injection de dépendances consiste à fournir les objets dont une classe a besoin plutôt que de laisser la classe les créer elle-même. Ce principe est crucial pour plusieurs raisons :
Modularité : Les classes ne dépendent pas directement les unes des autres. Cela permet de découpler les composants du système, facilitant ainsi les modifications et les extensions. Si une classe A dépend d’une classe B, au lieu de créer une instance de B à l’intérieur de A, A recevra une instance de B via l’injection de dépendances. Cela permet de remplacer facilement B par une autre classe implémentant la même interface sans changer le code de A.
Testabilité : Les dépendances peuvent être facilement remplacées par des versions mock lors des tests. Cela permet d’isoler les tests unitaires, de réduire les dépendances sur des ressources externes (comme les bases de données ou les services web), et d’augmenter la vitesse et la fiabilité des tests. Par exemple, si une classe A dépend d’une classe B pour fonctionner, vous pouvez injecter une fausse version de B qui retourne des valeurs prédéfinies, facilitant ainsi le test des fonctionnalités de A indépendamment de B.
Frameworks d'injection : Java & Kotlin
Plusieurs frameworks permettent de gérer l’injection de dépendances, notamment Dagger , qui est souvent utilisé avec Android sous le nom de Hilt . Bien que d’autres solutions existent, Dagger est particulièrement populaire pour ses performances et sa flexibilité. Les autres frameworks incluent Spring, Guice, et Koin, chacun ayant ses propres avantages et inconvénients. Dagger est particulièrement apprécié dans l’écosystème Android pour sa capacité à générer du code à la compilation, ce qui améliore les performances d’exécution par rapport aux frameworks qui utilisent la réflexion.
Exemple pratique : Dagger en action
Dagger utilise plusieurs annotations pour définir les points d’injection et les composants nécessaires à l’injection de dépendances. Voici les concepts de base :
Annotations Principales
@Inject : Cette annotation indique que les dépendances doivent être injectées automatiquement par Dagger. Elle peut être utilisée sur les constructeurs, les champs, ou les méthodes. Par exemple, si une classe A dépend d’une classe B, vous pouvez annoter le constructeur de A avec @Inject pour que Dagger fournisse une instance de B à A.
// Classe B qui fournit une fonctionnalité à la Classe A
class B @Inject constructor() {
fun doSomething() : String{return "Dépendance B exécutée"}
}
// Classe A dépend de Classe B
class A @Inject constructor(private val b : B) {
// Méthode de Classe A qui utilise Classe B
fun performAction() : String {
return b.doSomething() // Appelle une méthode de la classe B
}
}
@Component : Cette annotation définit une interface qui permet à Dagger de générer les graphes de dépendances. Un composant est une interface annotée avec @Component qui liste les classes qui peuvent demander des injections.
@Component interface AppComponent {
// Injecte les dépendances dans l'activité spécifiée
fun inject(activity : MainActivity)
// Fournit une instance de A
fun getA()
: A
}
Scopes
Les scopes contrôlent le nombre d’instances d’une classe. Par exemple, l’annotation @Singleton garantit qu’une seule instance d’une classe sera créée et partagée.
@Singleton : Cette annotation est utilisée pour indiquer que Dagger doit créer une seule instance de la classe et la réutiliser à chaque fois qu’une injection est demandée. Cela peut améliorer les performances en évitant la création répétée d’objets coûteux en termes de ressources.
@Singleton class Repository @Inject constructor() {
// ...
}
Scopes personnalisés : En plus de @Singleton, Dagger permet de définir des scopes personnalisés pour contrôler la durée de vie des objets en fonction des besoins de votre application. Par exemple, vous pouvez définir un scope @ActivityScope pour des objets qui doivent vivre aussi longtemps que l’activité.
@Scope @Retention(AnnotationRetention.RUNTIME) annotation class ActivityScope
@ActivityScope @Component interface ActivityComponent {
fun inject(activity : MainActivity)
}
En résumé, Dagger offre une manière structurée et efficace de gérer les dépendances dans une application, en utilisant des annotations pour définir clairement où et comment les dépendances doivent être injectées. Cela permet d’écrire du code plus propre, plus maintenable et plus facile à tester.
Scopes et optimisation avec Dagger
Pour illustrer l’utilisation de Dagger, considérons un projet où nous voulons injecter des dépendances dans un ViewModel et un Repository.
Étape 1 : Ajouter les Dépendances
Dans le fichier build.gradle, nous ajoutons les dépendances nécessaires pour Dagger :
dependencies{implementation "com.google.dagger:dagger:2.44.2" kapt
"com.google.dagger:dagger-compiler:2.44.2"}
Résultat
Étape 2 : Créer les Classes et les Annotations
Repository : Classe qui fournit les articles
class ArticleRepository @Inject
constructor(private val localDataSource : ArticleLocalDataSource,
private val remoteDataSource : ArticleRemoteDataSource) {
// Utilisation de localDataSource et remoteDataSource pour gérer les articles
}
Explication :
@Inject constructor : Cette annotation indique à Dagger que ce constructeur doit être utilisé pour créer des instances de ArticleRepository. Les paramètres localDataSource et remoteDataSource seront également fournis par Dagger.
Data Sources : Classes représentant les sources de données locales et distantes
class ArticleLocalDataSource @Inject constructor() {
// Accès aux données locales
}
class ArticleRemoteDataSource @Inject constructor() {
// Accès aux données distantes
}
Explication :
ArticleLocalDataSource : Une classe responsable de l’accès aux données locales.
ArticleRemoteDataSource : Une classe responsable de l’accès aux données distantes.
Composant : Interface définissant le graphe de dépendances
@Singleton @Component(modules = [AppModule::class]) interface ApplicationGraph {
fun getArticleRepository() : ArticleRepository
}
Explication :
@Singleton : Indique que Dagger doit créer une seule instance de chaque dépendance dans ce composant.
@Component : Cette annotation définit une interface qui permet à Dagger de générer un graphe de dépendances. Dagger génère une classe basée sur cette interface pour fournir les dépendances nécessaires.
interface ApplicationGraph : Interface qui décrit le graphe de dépendances.
fun getArticleRepository(): ArticleRepository : Méthode qui permet d’obtenir une instance de ArticleRepository.
Étape 3 : Utilisation de Dagger dans l’Application
Initialisation
class MyApplication : Application() {
// Création paresseuse du composant ApplicationGraph
val appGraph
: ApplicationGraph by lazy{DaggerApplicationGraph.create()} override fun
onCreate() {
super.onCreate()
// Autres initialisations si nécessaires
}
}
Explication :
MyApplication : Une classe qui étend Application pour initialiser Dagger au niveau de l’application.
val appGraph: ApplicationGraph by lazy : Initialise le graphe de dépendances de Dagger lorsque l’application est créée.
DaggerApplicationGraph.create() : Méthode générée par Dagger pour créer une instance de ApplicationGraph.
Injection des Dépendances
class MainActivity : AppCompatActivity() {
@Inject
lateinit var articleRepository: ArticleRepository
override fun onCreate(savedInstanceState: Bundle?) {
super
.onCreate(savedInstanceState) setContentView(
R.layout.activity_main) // Assurez-vous de définir le layout correct
// Injection des dépendances
(application as MyApplication)
.appGraph.inject(this)
// Utilisation de articleRepository
// Exemple d'utilisation : articleRepository.getArticles() ou autre
}
}
Explication :
Code | Description |
|---|---|
@Inject lateinit var articleRepository: ArticleRepository | Indique à Dagger d’injecter une instance de ArticleRepository dans cette propriété. |
override fun onCreate(savedInstanceState: Bundle?) | Méthode appelée lorsque l’activité est créée. |
(application as MyApplication).appGraph.inject(this) | Injection des dépendances dans MainActivity à l’aide du graphe de dépendances créé dans MyApplication. |
Gestion des Scopes
@Singleton class ArticleRepository @Inject
constructor(private val localDataSource : ArticleLocalDataSource,
private val remoteDataSource : ArticleRemoteDataSource) {
// Logique pour fournir les articles
// Exemple de méthode pour obtenir des articles
fun getArticles() : List {
// Exemple simple : combine les données locales et distantes
val localArticles = localDataSource.getArticles() val remoteArticles =
remoteDataSource.getArticles()
// Combiner ou traiter les articles comme nécessaire
return localArticles +
remoteArticles
}
}
Explication :
@Singleton : Indique que Dagger doit créer une seule instance de ArticleRepository et la réutiliser chaque fois qu’elle est nécessaire.
Déclaration dans le Composant
@Singleton @Component(modules = [AppModule::class]) interface ApplicationGraph {
// Méthode pour injecter les dépendances dans les activités ou autres
// composants
fun inject(activity : MainActivity)
// Méthode pour obtenir une instance de ArticleRepository
fun getArticleRepository()
: ArticleRepository
}
Explication :
Cela signifie qu’il n’y aura qu’une seule instance de ArticleRepository créée et partagée par Dagger dans toute l’application.
En utilisant l’annotation @Singleton, nous nous assurons que Dagger crée une seule instance de ArticleRepository et la réutilise à chaque fois qu’elle est nécessaire, optimisant ainsi l’utilisation des ressources et améliorant les performances de l’application.
Formez-vous gratuitement avec Alphorm !
Maîtrisez les compétences clés en IT grâce à nos formations gratuites et accélérez votre carrière dès aujourd'hui.
FAQ
Qu'est-ce que l'injection de dépendances et pourquoi est-ce important ?
Quels sont les avantages de Dagger par rapport à d'autres frameworks ?
Comment Dagger gère-t-il les scopes pour optimiser les objets ?
Comment commencer à utiliser Dagger dans un projet existant ?
Quels sont les concepts de base de Dagger à connaître ?
Conclusion
En utilisant Dagger, vous pouvez rendre votre code plus modulaire et testable. Quelle autre technologie d’injection de dépendances utiliseriez-vous pour améliorer vos projets ?