Question

Quelqu'un peut-il m'expliquer les avantages de l'utilisation d'un conteneur IOC par rapport au simple codage en dur de l'implémentation par défaut dans un constructeur par défaut?

En d'autres termes, qu'est-ce qui ne va pas avec ce code?

public class MyClass
{
    private IMyInterface _myInterface;

    public MyClass()
    {
        _myInterface = new DefaultMyInterface();
    }

    public MyClass(IMyInterface myInterface)
    {
        _myInterface = myInterface;
    }
}

Autant que je sache, cette classe prend assez en charge l’injection de constructeur, de sorte que les tests unitaires et les moqueries se font facilement. De plus, le constructeur par défaut supprime la surcharge de calcul du conteneur IOC (sans parler du processus complet qui est beaucoup plus transparent).

L'utilisation d'un conteneur IOC ne présente qu'un seul avantage: il vous faut fréquemment changer d'implémentation de la mise en oeuvre de vos interfaces. Est-ce que je manque quelque chose?

Était-ce utile?

La solution

L’idée d’IoC est de déléguer une partie des fonctionnalités de votre composant à une autre partie du système. Dans le monde IoC, vous avez des composants qui ne se connaissent pas. Votre exemple viole ceci, car vous créez un couplage étroit entre MyClass et une implémentation de IMyInterface. L'idée principale est que votre composant ne dispose d'aucune information sur son utilisation. Dans votre exemple, votre composant formule des hypothèses sur son utilisation.

En fait, cette approche peut fonctionner, mais combiner IoC et l’initialisation d’objets explicites n’est pas une bonne pratique, IMO.

IoC vous donne un couplage lâche en effectuant une liaison tardive au prix d'une clarté du code. Lorsque vous ajoutez un comportement supplémentaire à ce processus, cela rend les choses encore plus compliquées et peut conduire à des bugs lorsque certains composants peuvent potentiellement recevoir des objets avec un comportement indésirable ou non prévu.

Autres conseils

Choisissez un côté:)

En bref, IOC est recommandé. Le problème avec le code est que je ne peux pas échanger l'implémentation par défaut de la dépendance sans recompiler le code comme vous l'avez indiqué à la fin. IOC vous permet de modifier la configuration ou la composition de votre objet dans un fichier externe sans recompilation.
CIO prend en charge les "travaux de construction et d’assemblage". responsabilité du reste du code. Le but de IOC n'est pas de rendre votre code testable ... c'est un effet secondaire agréable. (Tout comme le code TDDed conduit à une meilleure conception)

Ce code n’est pas faux et vous pouvez toujours l’utiliser avec les frameworks Dependency Injection comme Spring et Guice.

De nombreux développeurs considèrent le fichier de configuration XML de Spring comme un avantage par rapport aux dépendances de câblage dans le code, car vous pouvez changer d'implémentation sans avoir besoin d'une étape de compilation. En réalité, cet avantage est obtenu lorsque plusieurs implémentations sont déjà compilées dans votre chemin de classe et que vous souhaitez choisir l'implémentation au moment du déploiement. Vous pouvez imaginer une situation dans laquelle un composant est fourni par une tierce partie après le déploiement. De même, il peut arriver que vous souhaitiez envoyer des implémentations supplémentaires sous forme de correctif après le déploiement.

Mais tous les frameworks DI n’utilisent pas la configuration XML. Par exemple, dans Google Guice, les modules sont écrits en tant que classes Java et doivent être compilés comme toute autre classe java.

Alors, quel est l’avantage de DI si vous avez même besoin d’une étape de compilation? Cela nous ramène à votre question initiale. Je peux voir les avantages suivants:

  1. Approche standard de DI tout au long de l'application.
  2. Configuration nettement séparée de toute autre logique.
  3. Possibilité d’injecter des procurations. Spring, par exemple, vous permet de gérer les transactions de manière déclarative en injectant des mandataires à la place de votre implémentation
  4. Réutilisation plus facile de la logique de configuration. Lorsque vous utilisez abondamment DI, vous verrez un arbre complexe de dépendances évoluer au fil du temps. Le gérer sans une couche de configuration et un support de structure clairement séparés peut être un cauchemar. Les infrastructures DI facilitent la réutilisation de la logique de configuration via l'héritage et d'autres moyens.

Mon seul souci est (1) si votre dépendance de service par défaut a elle-même une autre dépendance de service / secondaire (et ainsi de suite ... votre interface DefaultMyInterface dépend de ILogger) et (2) vous devez isoler la première dépendance de service de la seconde (nécessité de tester DefaultMyInterface avec un stog ILogger). Dans ce cas, vous devrez évidemment perdre la valeur par défaut "new DefaultMyInterface". et effectuez plutôt l'une des actions suivantes: (1) injection de dépendance pure ou (2) localisateur de service ou (3) conteneur.BuildUp (nouveau DefaultMyInterface ());

Certaines des préoccupations exprimées par d’autres affiches pourraient ne pas répondre à votre question. Vous ne posiez pas de questions sur la "production" multiple. mises en œuvre. Vous parlez de tests unitaires. Dans le cas où votre système serait testé, avec ma première mise en garde, cela semble légitime; J’envisagerais moi aussi de l’utiliser dans des cas de tests unitaires simples.

De même, quelques personnes interrogées ont exprimé leur inquiétude face à la capacité de se faire reprocher. Je n'aime pas les répétitions non plus, mais si (1) votre implémentation par défaut est vraiment votre implémentation par défaut (YAGNI: vous n'avez pas l'intention de modifier la valeur par défaut) et (2) vous ne croyez pas que la première mise en garde que j'ai énoncée s'applique et (3) préférez l’approche de code plus simple que vous avez partagée, alors je ne pense pas que cette forme particulière de répétition pose problème.

Outre le couplage lâche, un IoC réduira la duplication de code. Lorsque vous utilisez une IoC et que vous souhaitez modifier l'implémentation par défaut de votre interface, vous devez la modifier à un seul endroit. Lorsque vous utilisez des constructeurs par défaut pour injecter l'implémentation par défaut, vous devez la modifier partout où l'interface est utilisée.

En plus des autres commentaires, on pourrait argumenter le principe DRY (Ne vous répétez pas) dans ces cas-là. Il est inutile de mettre ce code de construction par défaut dans chaque classe. C'est aussi une introduction au traitement des cas spéciaux où il n'est pas nécessaire qu'il y en ait.

Je ne vois pas pourquoi votre technique de codage en dur de l'implémentation par défaut ne pourrait pas être utilisée avec un conteneur IOC. Simplement, les dépendances que vous ne spécifiez pas dans la configuration prendraient l’implémentation par défaut.

Ou est-ce que je manque quelque chose?

L’utilisation d’un conteneur IOC pourrait notamment servir à faciliter la configuration tardive de votre logiciel.

Supposons, par exemple, que vous fournissiez plusieurs implémentations d'une interface particulière. Le client (ou votre équipe de services professionnels) peut choisir celle à utiliser en modifiant le fichier de configuration IOC.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top