JavaRush /Blog Java /Random-FR /Un moyen simple d'injecter des dépendances

Un moyen simple d'injecter des dépendances

Publié dans le groupe Random-FR
L'injection de dépendances (DI) n'est pas un concept facile à comprendre, et son application à des applications nouvelles ou existantes est encore plus déroutante. Jess Smith vous montre comment effectuer une injection de dépendances sans conteneur d'injection dans les langages de programmation C# et Java. Un moyen simple d'injection de dépendances - 1Dans cet article, je vais vous montrer comment implémenter l'injection de dépendances (DI) dans les applications .NET et Java. Le concept d'injection de dépendances a attiré l'attention des développeurs pour la première fois en 2000, lorsque Robert Martin a écrit l'article « Design Principles and Patterns » (connu plus tard sous l'acronyme SOLID ). Le D dans SOLID fait référence à la dépendance d'inversion (DOI), qui est devenue plus tard connue sous le nom d'injection de dépendance. La définition originale et la plus courante : l'inversion de dépendances est une inversion de la façon dont une classe de base gère les dépendances. L'article original de Martin utilisait le code suivant pour illustrer la dépendance d'une classe Copyà une classe de niveau inférieurWritePrinter :
void Copy()
	{
	 int c;
	 while ((c = ReadKeyboard()) != EOF)
		WritePrinter(c);
	}
Le premier problème évident est que si vous modifiez la liste des paramètres ou les types d'une méthode WritePrinter, vous devez implémenter des mises à jour partout où il existe une dépendance à cette méthode. Ce processus augmente les coûts de maintenance et constitue une source potentielle de nouvelles erreurs.
Vous souhaitez en savoir plus sur Java ? Rejoignez le groupe Développeur Java !
Autre problème : la classe Copy n'est plus une candidate potentielle à la réutilisation. Par exemple, que se passe-t-il si vous devez imprimer les caractères saisis au clavier vers un fichier plutôt que vers une imprimante ? Pour ce faire, vous pouvez modifier la classe Copycomme suit (syntaxe du langage C++) :
void Copy(outputDevice dev)
	{
	int c;
	while ((c = ReadKeyboard()) != EOF)
		if (dev == printer)
			WritePrinter(c);
		else
			WriteDisk(c);
	}
Malgré l'introduction d'une nouvelle dépendance WriteDisk, la situation ne s'est pas améliorée (mais s'est plutôt aggravée) car un autre principe a été violé : « les entités logicielles, c'est-à-dire les classes, les modules, les fonctions, etc., doivent être ouvertes à l'extension, mais fermées à l'extension. modification." Martin explique que ces nouvelles instructions conditionnelles if/else réduisent la stabilité et la flexibilité du code. La solution consiste à inverser les dépendances afin que les méthodes d'écriture et de lecture dépendent du fichier Copy. Au lieu de « faire apparaître » les dépendances, elles sont transmises via le constructeur. Le code modifié ressemble à ceci :
class Reader
	{
		public:
		virtual int Read() = 0;
	};
	class Writer
	{
		public:
		virtual void Write(char) = 0;
	};
	void Copy(Reader& r, Writer& w)
	{
		int c;
		while((c=r.Read()) != EOF)
		w.Write(c);
	}
Désormais, la classe Copypeut être facilement réutilisée avec différentes implémentations de méthodes de classe Readeret Writer. La classe Copyne dispose d'aucune information sur la structure interne des types Readeret Writer, ce qui permet de les réutiliser avec différentes implémentations. Mais si tout cela vous semble être une sorte de charabia, peut-être que les exemples suivants en Java et C# clarifieront la situation.

Exemple en Java et C#

Pour illustrer la facilité de l'injection de dépendances sans conteneur de dépendances, commençons par un exemple simple qui peut être personnalisé pour être utilisé DIen quelques étapes seulement. Disons que nous avons une classe HtmlUserPresentationqui, lorsque ses méthodes sont appelées, génère une interface utilisateur HTML. Voici un exemple simple :
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
Tout projet utilisant ce code de classe aura une dépendance sur la classe HtmlUserPresentation, entraînant les problèmes d'utilisabilité et de maintenabilité décrits ci-dessus. Une amélioration s'impose immédiatement : créer une interface avec les signatures de toutes les méthodes actuellement disponibles dans la classe HtmlUserPresentation. Voici un exemple de cette interface :
public interface IHtmlUserPresentation {
	String createTable(ArrayList rowVals, String caption);
	String createTableRow(String tableCol);
	// Оставшиеся сигнатуры
}
Après avoir créé l'interface, nous modifions la classe HtmlUserPresentationpour l'utiliser. Revenant à l'instanciation du type HtmlUserPresentation, nous pouvons maintenant utiliser le type d'interface au lieu du type de base :
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
La création d'une interface nous permet d'utiliser facilement d'autres implémentations du IHtmlUserPresentation. Par exemple, si nous voulons tester ce type, nous pouvons facilement remplacer le type de base HtmlUserPresentationpar un autre type appelé HtmlUserPresentationTest. Les modifications apportées jusqu'à présent rendent le code plus facile à tester, à maintenir et à mettre à l'échelle, mais ne font rien pour la réutilisation puisque toutes les HtmlUserPresentationclasses utilisant le type sont toujours conscientes de son existence. Pour supprimer cette dépendance directe, vous pouvez passer un type d'interface IHtmlUserPresentationau constructeur (ou liste de paramètres de méthode) de la classe ou de la méthode qui l'utilisera :
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
Le constructeur UploadFilea désormais accès à toutes les fonctionnalités du type IHtmlUserPresentation, mais ne connaît rien de la structure interne de la classe qui implémente cette interface. Dans ce contexte, l'injection de type se produit lorsqu'une instance de classe est créée UploadFile. Un type d'interface IHtmlUserPresentationdevient réutilisable en passant différentes implémentations à différentes classes ou méthodes qui nécessitent des fonctionnalités différentes.

Conclusion et recommandations pour consolider le matériel

Vous avez appris l'injection de dépendances et que les classes dépendent directement les unes des autres lorsque l'une d'elles en instancie une autre pour accéder aux fonctionnalités du type cible. Pour découpler la dépendance directe entre les deux types, vous devez créer une interface. Une interface donne à un type la possibilité d'inclure différentes implémentations, en fonction du contexte de la fonctionnalité requise. En passant un type d'interface à un constructeur de classe ou à une méthode, la classe/méthode qui a besoin de la fonctionnalité ne connaît aucun détail sur le type implémentant l'interface. Pour cette raison, un type d’interface peut être réutilisé dans différentes classes qui nécessitent un comportement similaire, mais pas identique.
  • Pour expérimenter l'injection de dépendances, examinez votre code à partir d'une ou plusieurs applications et essayez de convertir un type de base très utilisé en interface.

  • Modifiez les classes qui instancient directement ce type de base pour utiliser ce nouveau type d'interface et transmettez-le via le constructeur ou la liste de paramètres de la méthode de classe qui l'utilisera.

  • Créez une implémentation de test pour tester ce type d'interface. Une fois votre code refactorisé, DIil deviendra plus facile à mettre en œuvre et vous remarquerez à quel point votre application devient plus flexible en termes de réutilisation et de maintenabilité.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION