Vendez-moi sur des conteneurs IoC, s'il vous plaît


17

J'ai vu plusieurs recommander l'utilisation de conteneurs IoC dans le code. La motivation est simple. Prenez le code injecté de dépendance suivant:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest(
        std::auto_ptr<Dependency> d = std::auto_ptr<Dependency>(new ConcreteDependency)
    ) : d_(d)
    {
    }
};


TEST(UnitUnderTest, Example)
{
    std::auto_ptr<Dependency> dep(new MockDependency);
    UnitUnderTest uut(dep);
    //Test here
}

Dans:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest()
    {
        d_.reset(static_cast<Dependency *>(IocContainer::Get("Dependency")));
    }
};


TEST(UnitUnderTest, Example)
{
    UnitUnderTest uut;
    //Test here
}

//Config for IOC container normally
<Dependency>ConcreteDependency</Dependency>

//Config for IOC container for testing
<Dependency>MockDependency</Dependency>

(Ce qui précède est un exemple hypothétique C ++ bien sûr)

Bien que je convienne que cela simplifie l'interface de la classe en supprimant le paramètre constructeur de dépendance, je pense que le remède est pire que la maladie pour deux raisons. Tout d'abord, et c'est un gros problème pour moi, cela rend votre programme dépendant d'un fichier de configuration externe. Si vous avez besoin d'un déploiement binaire unique, vous ne pouvez tout simplement pas utiliser ces types de conteneurs. Le deuxième problème est que l'API est désormais faiblement et pire, typée de manière stricte . La preuve (dans cet exemple hypothétique) est l'argument chaîne du conteneur IoC et la conversion du résultat.

Alors .. y a-t-il d'autres avantages à utiliser ces types de conteneurs ou est-ce que je suis en désaccord avec ceux qui recommandent les conteneurs?


1
L'exemple de code ressemble à un très mauvais exemple d'implémentation IoC. Je ne connais que C # mais il y a sûrement un moyen d'avoir l'injection de constructeur et la configuration programmatique en C ++ également?
rmac

Réponses:


12

Dans une grande application avec de nombreuses couches et beaucoup de pièces mobiles, ce sont les inconvénients qui commencent à sembler assez mineurs par rapport aux avantages.

Le conteneur "simplifie l'interface" de la classe, mais il le fait de manière très importante. Le conteneur est une solution au problème créé par l'injection de dépendances, qui est la nécessité de transmettre les dépendances partout, à travers les graphiques d'objets et à travers les zones fonctionnelles. Vous avez un petit exemple ici qui a une dépendance - si cet objet avait trois dépendances, et les objets qui en dépendait avait plusieurs objets qui dépendaient eux , et ainsi de suite? Sans conteneur, les objets situés en haut de ces chaînes de dépendance finissent par devenir responsables du suivi de toutes les dépendances dans l'application entière.

Il existe également différents types de conteneurs. Tous ne sont pas typés de façon stricte et ne nécessitent pas tous des fichiers de configuration.


3
Mais un grand projet aggrave les problèmes que j'ai déjà mentionnés . Si le projet est volumineux, le fichier de configuration devient lui-même un énorme aimant de dépendance, et il devient encore plus facile qu'une seule faute de frappe entraîne un comportement indéfini lors de l'exécution. (c'est-à-dire que le fait d'avoir une faute de frappe dans le fichier de configuration entraînerait un casting non défini si le mauvais type est retourné) Quant aux dépendances des dépendances, elles n'ont pas d'importance ici. Soit le constructeur se charge de les créer, soit le test se moquera de la dépendance de niveau supérieur.
Billy ONeal

... Suite ... Lorsque le programme n'est pas testé, la dépendance concrète par défaut est instanciée à chaque étape. Par rapport à d'autres types de conteneurs, avez-vous des exemples?
Billy ONeal

4
TBH Je n'ai pas beaucoup d'expérience avec les implémentations DI, mais jetez un œil à MEF sur .NET, qui peut mais ne doit pas être typé de manière stricte, n'a pas de fichier de configuration et les implémentations concrètes des interfaces sont "enregistrées" sur les classes lui-même. BTW, quand vous dites: "le constructeur s'occupe de les fabriquer" - si c'est ce que vous faites (alias "injection du constructeur du pauvre"), alors vous n'avez pas besoin d'un conteneur. L'avantage d'un conteneur par rapport à cela est que le conteneur centralise les informations sur toutes ces dépendances.
nlawalker

+1 pour ce dernier commentaire - la dernière phrase, il y a la réponse :)
Billy ONeal

1
Excellente réponse qui m'a aidé à comprendre tout le problème. Il ne s'agit pas simplement de simplifier le passe-partout, mais de permettre aux éléments situés en haut de la chaîne de dépendance d'être tout aussi naïfs que les éléments situés en bas.
Christopher Berman

4

Le framework Guice IoC de Java ne repose pas sur un fichier de configuration mais sur un code de configuration . Cela signifie que la configuration n'est pas différente du code composant votre application réelle et peut être refactorisée, etc.

Je crois que Guice est le framework Java IoC qui a finalement obtenu la bonne configuration.


Existe-t-il des exemples similaires pour C ++?
Billy ONeal

@Billy, je ne connais pas assez bien le C ++ pour pouvoir le dire.

0

Cette excellente réponse SO de Ben Scheirman détaille quelques exemples de code en C #; certains avantages des conteneurs IoC (DI) étant:

  • Votre chaîne de dépendances peut devenir imbriquée et il devient rapidement difficile de les câbler manuellement.
  • Permet l'utilisation de la programmation orientée Aspect .
  • Transactions de bases de données déclaratives et imbriquées.
  • Unité de travail déclarative et imbriquée.
  • Enregistrement.
  • Conditions de pré / post (Design by Contract).

1
Je ne vois pas comment aucune de ces choses ne peut être implémentée avec une simple injection de constructeur.
Billy ONeal

Avec une bonne conception, l'injection de constructeur ou une autre méthode peut vous suffire; les avantages de ces conteneurs IoC peuvent être uniquement dans des cas spécifiques. L'exemple donné avec INotifyPropertyChanged dans un projet WPF en est un exemple.
dodgy_coder
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.