Recommandations sur l'intégration du conteneur DI / IoC dans une application existante

10

Je suis maintenant confronté à l'intégration d'un conteneur d'inversion de contrôle (IoC) dans une application existante, et je cherche des recommandations sur la façon dont cela peut être réalisé le plus facilement dans le but ultime de réduire le couplage, augmentant ainsi la testabilité. Bien que je ne classifie généralement pas la plupart des classes comme des objets divins , chacune a trop de responsabilités et de dépendances cachées par le biais de la statique, des singletons et du manque d'interfaces.

Voici un aperçu de certains des défis qui doivent être relevés:

  • L'injection de dépendance est rarement utilisée
  • Les méthodes statiques abondent - à la fois comme méthodes d'usine et méthodes d'assistance
  • Les singletons sont assez répandus
  • Les interfaces, lorsqu'elles sont utilisées, ne sont pas trop granulaires
  • Les objets récupèrent souvent des dépendances inutiles via les classes de base

Notre intention est que la prochaine fois que nous devons apporter des changements dans un domaine particulier, nous essayons de démêler les dépendances qui, en réalité, existent mais sont cachées derrière des globaux tels que les singletons et la statique.

Je suppose que cela rend le conteneur IoC secondaire à l'introduction de l'injection de dépendance, mais je m'attends à ce qu'il y ait un ensemble de pratiques et de recommandations qui pourraient être suivies ou envisagées qui nous aideront à briser ces dépendances.

Kaleb Pederson
la source
3
Je dois demander pourquoi maintenant… qu'est-ce qui motive ce changement? Maintenabilité? Évolutivité puisqu'elle va croître de façon exponentielle? Les développeurs s'ennuient?
Aaron McIver,
1
Je viens de rejoindre la société récemment et j'essaie de vous présenter quelques "bonnes pratiques". Mon objectif principal est d'augmenter la testabilité et de réduire le couplage. Nous pouvons facilement utiliser DI / IoC pour le nouveau code et n'avons pas l'intention d'essayer de changer tout le code existant à la fois, mais j'aimerais des recommandations sur la meilleure façon de changer le code existant pour le mieux la prochaine fois que nous faisons changements dans ce domaine. Jusqu'à présent, la seule chose que j'ai trouvée en ligne est la suivante: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson
3
À moins qu'il n'y ait un grand nombre de tests unitaires / d'intégration automatisés en place; la modification de l'infrastructure existante, sauf s'il existe un problème au nom des meilleures pratiques, pose des problèmes. Même si votre suite de tests unitaires / d'intégration était solide, j'étais encore hésitante.
Aaron McIver,
1
@Aaron J'espère que cela ne ressemblait pas à ce que nous faisons pour le bien des meilleures pratiques. Nous apportons des modifications car il est difficile et lent de travailler avec le code existant et de le faire au coup par coup pendant que nous travaillons dans ce domaine particulier. Heureusement, nous avons un ensemble de tests d'intégration raisonnables et quelques tests unitaires pour prendre en charge les modifications.
Kaleb Pederson

Réponses:

8

Pour réussir quelque chose comme ça, vous devrez travailler par étapes, chacune d'elles n'est pas anodine mais elles peuvent être accomplies. Avant de commencer, vous devez comprendre que vous ne pouvez pas précipiter ce processus.

  1. Définissez les principaux sous-systèmes de l'application et un interfacepour chacun d'eux. Cette interface doit seulement définir les méthodes que d' autres parties du système utiliseront pour lui parler. REMARQUE: vous devrez peut-être effectuer plusieurs passes à ce stade.
  2. Fournissez une implémentation wrapper de cette interface qui délègue au code existant. Le but de cet exercice est d'éviter la réécriture en masse , mais de refactoriser le code pour utiliser les nouvelles interfaces - c'est-à-dire réduire le couplage dans votre système.
  3. Configurez le conteneur IoC pour construire le système à l'aide des interfaces et des implémentations que vous avez créées. À ce stade, vous voulez prendre soin d'instancier le conteneur IoC afin qu'il puisse afficher l'application. Autrement dit, si vous êtes dans un environnement de servlet, assurez-vous que vous pouvez obtenir / créer le conteneur dans la init()méthode de servlet .
  4. Faites à nouveau la même chose dans chaque sous-système, cette fois lorsque vous refactorisez, vous transformez votre implémentation de stub en une chose réelle qui à son tour utilise des interfaces pour parler à ses composants.
  5. Répétez autant de fois que nécessaire jusqu'à ce que vous ayez un bon équilibre entre la taille des composants et la fonctionnalité.

Lorsque vous avez terminé, les seules méthodes statiques que vous devriez avoir dans votre système sont celles qui sont vraiment des fonctions - par exemple, regardez la Collectionsclasse ou la Mathclasse. Aucune méthode statique ne doit tenter d'accéder directement aux autres composants.

C'est quelque chose qui prendra beaucoup de temps, planifiez en conséquence. Assurez-vous que lorsque vous effectuez votre refactoring, vous devenez plus SOLIDE dans votre approche de conception. Au début, ce sera douloureux. Vous modifiez radicalement la conception de votre application de manière itérative.

Berin Loritsch
la source
Bonnes suggestions. Je renvoie les autres aux suggestions ici également lors de ces modifications: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson
7

Choisissez Travailler efficacement avec Legacy Code et suivez ses conseils. Commencez par créer des îlots de code couvert et évoluez progressivement vers une application plus bien structurée. Essayer de faire le changement en masse est une recette pour un désastre.

Michael Brown
la source
J'adore ce livre !!
Martijn Verburg
Bonne recommandation. Bien que j'en ai lu la moitié il y a des années, j'imagine que j'en tirerais beaucoup plus maintenant que je suis profondément immergé dans la situation.
Kaleb Pederson
C'est drôle, la réponse choisie résume essentiellement le livre;) Bien sûr, Feathers va beaucoup plus en détail.
Michael Brown
5

La principale raison de l'introduction de l'IoC est le découplage des modules. Le problème avec particulièrement Java est l'extraordinaire liaison forte que l' newopérateur donne, combinée avec cela, cela signifie que le code appelant sait exactement quel module il utilisera.

Des usines ont été introduites pour déplacer ces connaissances vers un emplacement central, mais à la fin vous avez toujours câblé les modules en utilisant new/ singleton qui conserve la liaison, ou vous lisez dans un fichier de configuration et utilisez la réflexion / Class.forName qui est fragile lors de la refactorisation .

Si vous n'avez pas la cible modulaire, IoC ne vous donnera rien.

L'introduction de tests unitaires changera très probablement cela, car vous devrez simuler des modules qui ne sont pas en cours de test, et la façon la plus simple de gérer cela ainsi que les modules de production réels avec le même code est d'utiliser un cadre IoC pour injecter le approprié modules.


la source