598 votes

Pourquoi ai-je besoin d'un conteneur IoC par opposition à la simple DI code?

J'ai été en utilisant l'Injection de Dépendance (DI) pour un certain temps, l'injection soit dans un constructeur, la propriété ou la méthode. Je n'ai jamais ressenti le besoin d'utiliser une Inversion de Contrôle (IoC) conteneur. Cependant, plus je lis, plus la pression que je ressens de la communauté à utiliser un conteneur IoC.

J'ai joué avec .NET des contenants comme StructureMap, NInject, l'Unité, et Funq. Je ne comprends toujours pas comment un conteneur IoC va profiter / améliorer mon code.

Je suis aussi peur de commencer à l'aide d'un récipient de travail que beaucoup de mes collègues va voir le code qu'ils ne comprennent pas. Beaucoup d'entre eux peuvent être réticents à apprendre de nouvelles technologies.

S'il vous plaît, de me convaincre que j'ai besoin d'utiliser un conteneur IoC. Je vais utiliser ces arguments quand je parle à mes collègues développeurs au travail.

441voto

Ben Scheirman Points 23590

Wow, ne peut pas croire que Joel serait en faveur de cette:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

au cours de cette:

var svc = IoC.Resolve<IShippingService>();

Beaucoup de gens ne réalisent pas que vos dépendances de la chaîne peut devenir imbriqués, et il devient rapidement lourd à câbler manuellement. Même avec des usines, de la duplication de code est juste pas la peine.

Cio de conteneurs peut être complexe, oui. Mais pour ce cas simple, j'ai montré qu'il est incroyablement facile.


Edit: ok, nous allons justifier encore davantage. Disons que vous avez certaines entités ou objets de modèle que vous souhaitez lier à une puce d'INTERFACE utilisateur. Cette puce d'INTERFACE utilisateur (que nous appellerons Shindows Morms) vous veut implémenter INotifyPropertyChanged afin qu'elle puisse faire le suivi des modifications et mise à jour de l'INTERFACE utilisateur en conséquence.

"OK, ce n'est pas dur" afin de commencer à écrire.

Vous commencez avec ceci:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..et à la fin jusqu'à ce:

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

C'est dégoûtant code de la plomberie, et je maintiens que si vous écrivez du code tel qu'il est par la main que vous avez volé à partir de votre client. Il y a mieux, plus intelligemment à la façon de travailler.

Jamais entendu parler de ce terme, de travailler plus intelligemment, pas plus fort?

Eh bien imaginez un gars intelligent sur votre équipe est venue et a dit: "Voici un moyen plus facile"

Si vous faites vos propriétés virtuel (calmez-vous, ce n'est pas que les grandes d'un accord), alors nous pouvons tisser dans ce comportement de la propriété automatiquement. (Cela s'appelle de l'AOP, mais ne vous inquiétez pas le nom, se concentrer sur ce qu'il va faire pour vous)

Selon le Cio de l'outil que vous utilisez, vous pourriez faire quelque chose qui ressemble à ceci:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Pouf! L'ensemble de ce manuel INotifyPropertyChanged BS est maintenant généré automatiquement pour vous, sur tous les biens virtuels setter de l'objet en question.

Est-ce de la magie? OUI! Si vous pouvez faire confiance au fait que ce code fait son travail, alors vous pouvez en toute sécurité ignorer tout de cette propriété emballage mumbo-jumbo. Vous avez d'affaires des problèmes à résoudre.

Quelques autres utilisations intéressantes de Cio outil pour faire de l'AOP:

  • Déclarative & nested transactions de base de données
  • Déclarative et imbriquées Unité de travail
  • La journalisation
  • Pré/Post-conditions (Design by contract)

138voto

Joel Spolsky Points 22686

Je suis avec vous, Vadim. Cio des conteneurs de prendre un simple, élégant et concept utile et faire quelque chose que vous avez à étudier pour deux jours avec un 200-manuel de la page.

Personnellement, je suis perplexe sur la façon dont le Cio de la communauté a pris une belle, élégante article de Martin Fowler et l'a transformé en un tas de complexes cadres généralement avec 200-300 page manuels.

J'essaie de ne pas être catégorique (HAHA!), mais je pense que les gens qui utilisent des conteneurs IoC sont très intelligents et (B) manque d'empathie pour les personnes qui ne sont pas aussi intelligents qu'ils le sont. Tout est parfaitement logique pour eux, donc ils ont de la difficulté à comprendre que beaucoup de programmeurs trouverez les concepts confus. C'est la malédiction de la connaissance. Les gens qui comprennent Cio conteneurs ont du mal à croire qu'il y a des gens qui ne la comprennent pas.

Les plus précieux avantages de l'utilisation d'un conteneur IoC est que vous pouvez avoir une configuration de commutateur dans un endroit qui vous permet de changer entre le mode test et mode de production. Par exemple, supposons que vous avez deux versions de votre accès à la base de classes... une version enregistrée de façon agressive et fait beaucoup de validation, que vous avez utilisé au cours du développement, et une autre version sans enregistrement ou de validation qui a été hurlement rapide de la production. Il est agréable d'être en mesure de basculer entre eux en un seul endroit. D'autre part, c'est assez trivial problème facilement traitées d'une manière simple, sans la complexité de la Coi conteneurs.

Je crois que si vous utilisez des conteneurs IoC, votre code devient, franchement, beaucoup plus difficile à lire. Le nombre de places que vous avez à regarder pour comprendre ce que le code est en train de faire va par au moins un. Et quelque part dans le ciel un ange pleure.

37voto

TrueWill Points 14855

Sans doute personne ne vous oblige à utiliser un conteneur d'injection de dépendances cadre. Vous êtes déjà à l'aide de DI dissocier vos classes et d'améliorer la testabilité, de sorte que vous obtenez de nombreux avantages. En bref, vous êtes en privilégiant la simplicité, ce qui est généralement une bonne chose.

Si votre système a atteint un niveau de complexité où manuel DI devient une corvée (qui est, les augmentations de maintenance), qui pèsent contre l'équipe de la courbe d'apprentissage d'un conteneur d'injection de dépendances cadre.

Si vous avez besoin de plus de contrôle sur la dépendance de gestion de durée de vie (qui est, si vous vous sentez le besoin d'implémenter le pattern Singleton), regardez DI conteneurs.

Si vous utilisez un conteneur d'injection de dépendances, utiliser uniquement les fonctionnalités dont vous avez besoin. Ignorer le fichier de configuration XML et de le configurer dans le code si c'est suffisant. Bâton de constructeur d'injection. Les bases de l'Unité ou de StructureMap peuvent être résumés à quelques pages.

Il y a un bon article de blog de Marque Seemann sur ce point: Lors de l'utilisation d'un Conteneur d'injection de dépendances

32voto

bendewey Points 25437

À mon avis, l'avantage du numéro un d'un Cio est la possibilité de centraliser la configuration de vos dépendances.

Si vous êtes actuellement à l'aide de l'injection de Dépendance de votre code pourrait ressembler à ceci

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Si vous avez utilisé un statique Cio classe, par opposition à l', à mon humble avis le plus déroutant, fichiers de configuration, vous pouvez avoir quelque chose comme ceci:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Ensuite, votre Statique Cio classe devrait ressembler à ceci, je suis en utilisant l'Unité ici.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

32voto

bendewey Points 25437

Cio Conteneurs sont également bonnes pour le chargement profondément imbriqués classe dépendances. Par exemple si vous avez le code suivant à l'aide de Depedency Injection.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Si vous avez eu toutes ces dépendances chargé dans et conteneur IoC vous pourriez Résoudre le CustomerService et l'enfant dépendances sera automatiquement résolu.

Par exemple:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X