Injection de dépendance vs modèle d'usine


498

La plupart des exemples cités pour l'utilisation de l'injection de dépendance, nous pouvons également résoudre en utilisant le modèle d'usine. Il semble qu'en matière d'utilisation / de conception, la différence entre l'injection de dépendance et l'usine soit floue ou mince.

Une fois que quelqu'un m'a dit que c'est la façon dont vous l'utilisez qui fait la différence!

J'ai utilisé une fois StructureMap un conteneur DI pour résoudre un problème, plus tard, je l'ai repensé pour qu'il fonctionne avec une simple usine et j'ai supprimé les références à StructureMap.

Quelqu'un peut-il me dire quelle est la différence entre eux et où utiliser quoi, quelle est la meilleure pratique ici?


22
Ces deux approches ne peuvent-elles pas se compléter: utiliser l'injection de dépendance pour injecter des classes d'usine?
Richard Ev

20
Ce serait vraiment bien si cette question avait une réponse avec du code! Je ne vois toujours pas en quoi DI serait bénéfique / différent de l'utilisation d'une usine pour la création? Vous n'aurez qu'à remplacer cette ligne dans la classe d'usine pour changer quel obj / implémentation est créé?
gideon

2
@gideon ne vous obligerait-il pas à compiler votre application, ou du moins le module contenant la classe d'usine?
acide lysergique

1
@liortal yep c'est vrai. A fait une longue étude sur DI depuis ce commentaire et maintenant je comprends que le DI prend la méthode d'usine une longueur d'avance.
gideon

1
Découvrez cette excellente réponse: stackoverflow.com/questions/4985455/… - il le dit très bien et fournit des exemples de code.
Luis Perez

Réponses:


294

Lorsque vous utilisez une usine, votre code est toujours responsable de la création d'objets. Par DI, vous sous-traitez cette responsabilité à une autre classe ou un framework, qui est distinct de votre code.


172
Le modèle DI ne nécessite aucun cadre. Vous pouvez faire DI en écrivant manuellement des usines qui font DI. Les cadres DI facilitent simplement les choses.
Esko Luontola

28
@Perpetualcoder - Merci @Esko - Ne vous laissez pas entraîner par le cadre de mots signifiant une bibliothèque tierce avancée.
willcodejavaforfood

4
+1 @willcode merci! Vous devez donc modifier les fichiers config / xml à la place. Je vois. Le lien wiki en.wikipedia.org/wiki/… définit le modèle d'usine commeManually-Injected Dependency
gideon

5
Je n'ai pas l'avantage de changer 1 ligne de XML par rapport à changer 1 ligne de code. Pourriez-vous élaborer?
Rafael Eyng

1
Répétez la réponse OP en remplaçant "DI" par "usine", cela a toujours du sens.
Edson Medina

219

Je suggérerais de garder les concepts clairs et simples. L'injection de dépendance est plus un modèle architectural pour coupler de manière lâche les composants logiciels. Le modèle d'usine n'est qu'un moyen de séparer la responsabilité de créer des objets d'autres classes à une autre entité. Le modèle d'usine peut être appelé comme un outil pour implémenter DI. L'injection de dépendances peut être implémentée de plusieurs manières, comme DI à l'aide de constructeurs, à l'aide de mappage de fichiers xml, etc.


4
C'est vrai que Factory Pattern est l'un des moyens d'implémenter l'injection de dépendance. Un autre avantage de l'utilisation de l'injection de dépendances par rapport au modèle d'usine est que les cadres DI vous donneraient la flexibilité nécessaire pour enregistrer vos abstractions par rapport à vos types de béton. Tels que Code comme Config, XML ou Configuration automatique. Cette flexibilité vous permettra de gérer la durée de vie de vos objets ou de décider comment et quand enregistrer vos interfaces.
Teoman shipahi

1
Le modèle d'usine offre une abstraction de niveau supérieur à DI. Une usine peut offrir des options de configuration tout comme DI, mais elle peut également choisir de masquer tous ces détails de configuration, afin que l'usine puisse décider de passer à une implémentation complètement différente sans que le client le sache. La DI seule ne le permet pas. DI oblige le client à spécifier les changements qu'il souhaite.
neurone

1
Bonne réponse. Beaucoup prêtent à confusion avec cette question: "Quels brevets dois-je choisir?" En fait, les modèles de conception ne sont qu'un moyen de résoudre un problème dans des situations appropriées. Si vous ne savez pas quel modèle de conception vous devez utiliser, ou "Où dois-je implémenter le modèle?" alors vous n'avez pas besoin de ça pour le moment.
Benyi

2
Je pense qu'il est plus précis de dire que Factory Pattern est un outil pour implémenter l'IoC (pas DI). Veuillez noter que DI est une forme d'IoC
Hooman Bahreini

185

Injection de dépendance

Au lieu d'instancier les pièces elles-mêmes, une voiture demande les pièces dont elle a besoin pour fonctionner.

class Car
{
    private Engine engine;
    private SteeringWheel wheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.engine = engine;
        this.wheel = wheel;
        this.tires = tires;
    }
}

Usine

Rassemble les pièces pour créer un objet complet et cache le type de béton à l'appelant.

static class CarFactory
{
    public ICar BuildCar()
    {
        Engine engine = new Engine();
        SteeringWheel steeringWheel = new SteeringWheel();
        Tires tires = new Tires();
        ICar car = new RaceCar(engine, steeringWheel, tires);
        return car;
    }   
}

Résultat

Comme vous pouvez le voir, les usines et DI se complètent.

static void Main()
{
     ICar car = CarFactory.BuildCar();
     // use car
}

Vous souvenez-vous des goldilocks et des trois ours? Eh bien, l'injection de dépendance est un peu comme ça. Voici trois façons de faire la même chose.

void RaceCar() // example #1
{
    ICar car = CarFactory.BuildCar();
    car.Race();
}

void RaceCar(ICarFactory carFactory) // example #2
{
    ICar car = carFactory.BuildCar();
    car.Race();
}

void RaceCar(ICar car) // example #3
{
    car.Race();
}

Exemple # 1 - C'est le pire car il masque complètement la dépendance. Si vous considérez la méthode comme une boîte noire, vous n'auriez aucune idée qu'elle nécessite une voiture.

Exemple # 2 - C'est un peu mieux car maintenant nous savons que nous avons besoin d'une voiture puisque nous passons dans une usine automobile. Mais cette fois, nous passons trop car toute la méthode a réellement besoin d'une voiture. Nous passons dans une usine juste pour construire la voiture alors que la voiture pourrait être construite en dehors de la méthode et transmise.

Exemple # 3 - Ceci est idéal car la méthode demande exactement ce dont elle a besoin. Pas trop ou trop peu. Je n'ai pas besoin d'écrire une MockCarFactory juste pour créer des MockCars, je peux passer directement la maquette. C'est direct et l'interface ne ment pas.

Ce Google Tech Talk de Misko Hevery est incroyable et est la base de ce dont j'ai tiré mon exemple. http://www.youtube.com/watch?v=XcT4yYu_TTs


1
Le motif d'usine est injecté en tant que DI.
Mangesh Pimpalkar

7
@PhilGoetz Ce que vous décrivez ressemble plus au modèle Service Locator. Ils ont des objectifs similaires, en ce sens qu'ils visent à dissocier les services de leurs consommateurs. Cependant, le modèle Service Locator présente de nombreux inconvénients. Surtout, masquer les dépendances n'est pas une bonne chose. Le consommateur doit toujours obtenir ses dépendances, mais au lieu de définir une interface claire, elles sont transmises «secrètement» et dépendent de l'état global. Dans l'exemple, le consommateur n'a aucune idée de l'usine, il demande seulement ce dont il a besoin et n'a pas à se préoccuper de la façon dont ce besoin est construit.
Despertar

1
@MatthewWhited La plupart des décisions de conception ont un compromis. Avec DI, vous gagnez en flexibilité, en transparence et en un système plus testable, mais au prix d'exposer vos tripes. Beaucoup de gens trouvent cela un compromis acceptable. Cela ne veut pas dire que DI est toujours la meilleure solution et cette question n'est pas à ce sujet. L'exemple est destiné à montrer la différence entre DI et le modèle d'usine en montrant quelques bonnes et mauvaises utilisations de chacun.
Despertar

3
Ce n'est pas un modèle DI, c'est juste une implémentation d'IoC. DI utilise un ServiceLocator pour déterminer la bonne dépendance et l'injecter dans le constructeur lors de la création d'objet, votre code sépare uniquement la création d'objets de votre portée de classe.
Diego Mendes

3
@DiegoMendes Ce que vous décrivez est un cadre qui automatise DI. Le ServiceLocator, comme vous l'appelez, fait l'ID pour vous. Ce que je montre, c'est le modèle lui-même qui ne nécessite aucun cadre sophistiqué. Voir stackoverflow.com/a/140655/1160036 , ou voir en.wikipedia.org/wiki/… : "[la liste des frameworks] supporte l'injection de dépendance mais n'est pas obligé de faire l'injection de dépendance". En outre, "Une injection est le passage d'une dépendance à un objet dépendant". Vous êtes en train de compliquer un concept magnifiquement simple.
Despertar

50

La raison pour laquelle l'injection de dépendance (DI) et les modèles d'usine sont similaires est qu'il s'agit de deux implémentations de l'inversion de contrôle (IoC) qui est une architecture logicielle. En termes simples, ce sont deux solutions au même problème.

Donc, pour répondre à la question, la principale différence entre le modèle Factory et DI est de savoir comment la référence d'objet est obtenue. Avec l'injection de dépendances comme son nom l'indique, la référence est injectée ou donnée à votre code. Avec Factory pattern, votre code doit demander la référence pour que votre code récupère l'objet. Les deux implémentations suppriment ou découplent la liaison entre le code et la classe ou le type sous-jacent de la référence d'objet utilisée par le code.

Il convient de noter que les modèles Factory (ou même les modèles Abstract Factory qui sont des usines qui renvoient de nouvelles usines qui renvoient des références d'objet) peuvent être écrits pour choisir dynamiquement ou se lier au type ou à la classe d'objet demandé au moment de l'exécution. Cela les rend très similaires (encore plus que DI) au modèle de localisateur de service qui est une autre implémentation de l'IoC.

Le modèle de conception Factory est assez ancien (en termes de logiciel) et existe depuis un certain temps. Depuis la récente popularité du modèle architectural IoC, il connaît une résurgence.

Je suppose qu'en ce qui concerne les modèles de conception IoC: les injecteurs injectent, les localisateurs localisent et les usines ont été refactorisées.


3
C'est la meilleure réponse ... les autres réponses ne mentionnent pas l'IoC ou ne reconnaissent pas que DI est une forme d'IoC.
Hooman Bahreini

Merci, lorsque j'ai étudié le CIO pour la première fois, j'ai presque crié que ce n'était qu'une autre forme du modèle d'usine, il s'avère qu'ils sont vraiment similaires les uns aux autres
Harvey Lin

40

Il y a des problèmes faciles à résoudre avec l'injection de dépendances qui ne sont pas si facilement résolus avec une suite d'usines.

Une partie de la différence entre, d'une part, l'inversion du contrôle et l'injection de dépendance (IOC / DI), et, d'autre part, un localisateur de services ou une suite d'usines (usine), est:

IOC / DI est un écosystème complet d'objets et de services de domaine en soi. Il met tout en place pour vous de la manière que vous spécifiez. Vos objets et services de domaine sont construits par le conteneur, et ne se construisent pas eux-mêmes: ils n'ont donc aucune dépendance sur le conteneur ou sur les usines. IOC / DI permet un degré de configurabilité extrêmement élevé, avec toute la configuration en un seul endroit (construction du conteneur) à la couche supérieure de votre application (l'interface graphique, l'interface Web).

Factory résume une partie de la construction des objets et services de votre domaine. Mais les objets et services de domaine sont toujours responsables de trouver comment se construire et comment obtenir tout ce dont ils dépendent. Toutes ces dépendances "actives" filtrent tout au long de toutes les couches de votre application. Il n'y a pas un seul endroit où aller pour tout configurer.


26

Un inconvénient de DI est qu'il ne peut pas initialiser des objets avec une logique. Par exemple, lorsque j'ai besoin de créer un personnage avec un nom et un âge aléatoires, DI n'est pas le choix par rapport au modèle d'usine. Avec les usines, nous pouvons facilement encapsuler l'algorithme aléatoire de la création d'objet, qui prend en charge l'un des modèles de conception appelé "Encapsuler ce qui varie".


22

La gestion du cycle de vie est l'une des responsabilités des conteneurs de dépendance en plus de l'instanciation et de l'injection. Le fait que le conteneur conserve parfois une référence aux composants après l'instanciation est la raison pour laquelle il est appelé un "conteneur", et non une usine. Les conteneurs d'injection de dépendance ne gardent généralement qu'une référence aux objets dont ils ont besoin pour gérer les cycles de vie ou qui sont réutilisés pour les injections futures, comme les singletons ou les masselottes. Lorsqu'il est configuré pour créer de nouvelles instances de certains composants pour chaque appel au conteneur, le conteneur oublie généralement simplement l'objet créé.

De: http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html


15

Je crois que DI est un type de couche d'abstraction sur les usines, mais ils offrent également des avantages au-delà de l'abstraction. Une véritable usine sait instancier un seul type et le configurer. Une bonne couche DI permet, grâce à la configuration, d'instancier et de configurer de nombreux types.

De toute évidence, pour un projet avec quelques types simples qui nécessitent une logique métier relativement stable dans leur construction, le modèle d'usine est simple à comprendre, à mettre en œuvre et fonctionne bien.

OTOH, si vous avez un projet contenant de nombreux types dont les implémentations vous attendez à changer souvent, DI vous donne la flexibilité grâce à sa configuration pour le faire au moment de l'exécution sans avoir à recompiler vos usines.


14

Théorie

Il y a deux points importants à considérer:

  1. Qui crée des objets

    • [Factory]: Vous devez écrire COMMENT un objet doit être créé. Vous avez une classe Factory distincte qui contient la logique de création.
    • [Injection de dépendances]: Dans des cas pratiques, cela se fait par des frameworks externes (par exemple en Java qui serait spring / ejb / guice). L'injection se produit "comme par magie" sans création explicite de nouveaux objets
  2. Quel type d'objets il gère:

    • [Factory]: généralement responsable de la création d'objets avec état
    • [Injections de dépendance] Plus susceptibles de créer des objets sans état

Exemple pratique d'utilisation de l'injection d'usine et de dépendance dans un projet unique

  1. Ce que nous voulons construire

Module d'application pour créer une commande qui contient plusieurs entrées appelées ligne de commande.

  1. Architecture

Supposons que nous voulons créer l'architecture de couche suivante:

entrez la description de l'image ici

Les objets de domaine peuvent être des objets stockés dans la base de données. Le référentiel (DAO) aide à la récupération des objets de la base de données. Le service fournit une API à d'autres modules. Permet d'effectuer des opérations sur le ordermodule

  1. Couche de domaine et utilisation des usines

Les entités qui figureront dans la base de données sont Order et OrderLine. La commande peut avoir plusieurs lignes de commande. Relation entre Order et OrderLine

Vient maintenant une partie importante de la conception. Les modules en dehors de celui-ci doivent-ils créer et gérer des lignes de commande par eux-mêmes? Non. La ligne de commande ne doit exister que lorsque vous avez une commande associée. Il serait préférable que vous puissiez masquer l'implémentation interne aux classes externes.

Mais comment créer une commande sans connaître les lignes de commande?

Usine

Quelqu'un qui veut créer une nouvelle commande a utilisé OrderFactory (qui cachera des détails sur la façon dont nous créons la commande).

entrez la description de l'image ici

C'est à quoi cela ressemblera à l'intérieur de l'IDE. Les classes à l'extérieur du domainpackage utiliseront à la OrderFactoryplace du constructeur à l'intérieurOrder

  1. Injection de dépendance L'injection de dépendance est plus couramment utilisée avec les couches sans état telles que le référentiel et le service.

OrderRepository et OrderService sont gérés par un framework d'injection de dépendances. Le référentiel est responsable de la gestion des opérations CRUD sur la base de données. Le service injecte le référentiel et l'utilise pour enregistrer / trouver les classes de domaine correctes.

entrez la description de l'image ici


Pouvez-vous clarifier cela? [Factory]: Usually responsible for creation of stateful objects [Dependency Injections] More likely to create stateless objectsJe ne comprends pas pourquoi
Maksim Dmitriev

2
Les objets avec état sont susceptibles de se trouver dans la couche d'application de domaine où vous mappez vos données à la base de données, vous n'y utilisez généralement pas d'injection de dépendance. Factory est souvent utilisé pour créer AggregateRoot à partir d'un arbre d'objets complexes
Marcin Szymczak

12

Je sais que cette question est ancienne mais je voudrais ajouter mes cinq cents,

Je pense que l'injection de dépendance (DI) est à bien des égards comme un modèle d'usine configurable (FP), et dans ce sens, tout ce que vous pourriez faire avec DI, vous pourrez le faire avec une telle usine.

En fait, si vous utilisez Spring par exemple, vous avez la possibilité d'auto-câbler les ressources (DI) ou de faire quelque chose comme ceci:

MyBean mb = ctx.getBean("myBean");

Et puis utilisez cette instance «mb» pour faire quoi que ce soit. N'est-ce pas un appel à une usine qui vous rendra une instance ??

La seule vraie différence que je remarque entre la plupart des exemples FP est que vous pouvez configurer ce que "myBean" est dans un xml ou dans une autre classe, et un framework fonctionnera comme l'usine, mais à part ça c'est la même chose, et vous peut avoir certainement une fabrique qui lit un fichier de configuration ou obtient l'implémentation selon ses besoins.

Et si vous me demandez mon avis (et je sais que vous ne l'avez pas fait), je crois que DI fait la même chose mais ajoute juste plus de complexité au développement, pourquoi?

Eh bien, pour une chose, pour que vous sachiez quelle est l'implémentation utilisée pour n'importe quel bean que vous avez câblé avec DI, vous devez aller à la configuration elle-même.

mais ... qu'en est-il de cette promesse que vous n'aurez pas à connaître l'implémentation de l'objet que vous utilisez? pfft! sérieusement? lorsque vous utilisez une approche comme celle-ci ... n'êtes-vous pas le même qui écrit l'implémentation ?? et même si vous ne le faites pas, ne voyez-vous pas tout le temps comment l'implémentation fait ce qu'elle est censée faire ??

et pour une dernière chose, peu importe combien un framework DI vous promet que vous construirez des choses découplées de celui-ci, sans dépendances de leurs classes, si vous utilisez un framework vous construisez tout autour, si vous devez changer l'approche ou le cadre ce ne sera pas une tâche facile ... JAMAIS! ... mais, puisque vous construisez tout autour de ce cadre particulier au lieu de vous soucier de la meilleure solution pour votre entreprise, vous serez confronté à un problème biologique lors de cette opération.

En fait, la seule vraie application métier pour une approche FP ou DI que je puisse voir est si vous devez changer les implémentations utilisées lors de l' exécution , mais au moins les frameworks que je connais ne vous permettent pas de le faire, vous devez quitter tout est parfait dans la configuration au moment du développement et si vous en avez besoin, utilisez une autre approche.

Donc, si j'ai une classe qui fonctionne différemment dans deux étendues dans la même application (disons, deux sociétés d'une holding), je dois configurer le framework pour créer deux beans différents et adapter mon code pour utiliser chacun. N'est-ce pas la même chose que si j'écrivais simplement quelque chose comme ceci:

MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise
MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise

le même que celui-ci:

@Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise
@Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise

Et ça:

MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise
MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise

Dans tous les cas, vous devrez changer quelque chose dans votre application, qu'il s'agisse de classes ou de fichiers de configuration, mais vous devrez le faire et le redéployer.

Ce ne serait pas bien de faire juste quelque chose comme ça:

MyBean mb = (MyBean)MyFactory.get("mb"); 

Et de cette façon, vous définissez le code de l'usine pour obtenir la bonne implémentation au moment de l'exécution en fonction de l'entreprise utilisateur connectée ?? Maintenant, ce serait utile. Vous pouvez simplement ajouter un nouveau pot avec les nouvelles classes et définir les règles peut-être même au moment de l'exécution (ou ajouter un nouveau fichier de configuration si vous laissez cette option ouverte), aucune modification des classes existantes. Ce serait une usine dynamique!

cela ne serait-il pas plus utile que d'avoir à écrire deux configurations pour chaque entreprise, et peut-être même d'avoir deux applications différentes pour chacune?

Vous pouvez me dire que je n'ai jamais besoin de faire le changement au moment de l'exécution, donc je configure l'application et si j'hérite de la classe ou utilise une autre implémentation, je change simplement la configuration et redéploie. Ok, cela peut aussi se faire avec une usine. Et soyez honnête, combien de fois faites-vous cela? peut-être seulement lorsque vous avez une application qui sera utilisée ailleurs dans votre entreprise et que vous allez transmettre le code à une autre équipe, et ils feront des choses comme ça. Mais bon, ça peut aussi se faire en usine, et ce serait encore mieux avec une usine dynamique !!

Quoi qu'il en soit, la section des commentaires est ouverte pour que vous me tuiez.


3
Trop de développeurs pensent que les frameworks d'injection de dépendance sont créés pour apporter quelque chose de nouveau. Comme vous l'expliquez bien, les usines traditionnelles (le plus souvent des usines abstraites) peuvent jouer le même rôle d'inversion de dépendance. Inversion vs injection? Pour moi, le seul "avantage" du framework d'Injection de dépendances est que nous n'avons pas à changer / recompiler le code (car le plus souvent configurable avec XML comme Spring) lorsqu'une dépendance est ajoutée / changée. Pourquoi éviter de recompiler? Afin d'éviter certaines erreurs humaines potentielles lors du changement de dépendance / usine. Mais avec nos excellents IDE, le refactoring fonctionne bien :)
Mik378

6

Le CIO est un concept mis en œuvre de deux manières. La création de dépendances et l'injection de dépendances, Factory / Abstract factory sont l'exemple de création de dépendances. L'injection de dépendances est constructeur, setter et interface. Le cœur d'IOC ne doit pas dépendre des classes concrètes, mais définir l'abstrait des méthodes (disons une interface / classe abstraite) et utiliser cet abstrait pour appeler la méthode de la classe concrète. Comme le modèle Factory, retourne la classe ou l'interface de base. L'injection de dépendances similaires utilise la classe / interface de base pour définir la valeur des objets.


4

Avec l'injection de dépendances, le client n'a pas besoin d'obtenir ses dépendances par lui-même, tout est préparé à l'avance.

Avec les usines, quelqu'un doit les appeler pour acheminer les objets générés là où ils sont nécessaires.

La différence réside principalement dans cette seule ligne où l'appel de l'usine et la récupération de l'objet construit sont effectués.

Mais avec les usines, vous devez écrire cette 1 ligne partout où vous avez besoin d'un tel objet. Avec DI, il vous suffit de créer le câblage (relation entre l'utilisation et l'objet créé) une fois et de vous fier à la présence de l'objet par la suite partout. D'un autre côté, DI nécessite souvent un peu plus (ce qui dépend du cadre) de travail sur la préparation.


3

J'ai eu la même question dès que j'ai lu sur DI et je me suis retrouvé à ce poste. Alors finalement c'est ce que j'ai compris mais corrigez-moi s'il vous plaît.

"Il y a longtemps, il y avait de petits royaumes avec leurs propres organes directeurs contrôlant et prenant des décisions sur la base de leurs propres règles écrites. Plus tard, ils ont formé un grand gouvernement éliminant tous ces petits organes directeurs qui ont un ensemble de règles (constitution) et sont mis en œuvre par les tribunaux"

Les organes directeurs des petits royaumes sont des «usines»

Le grand gouvernement est le "Dependency Injector".


1
Alors, quand un codsider un petit gouvernement précédent à être maintenant gros? Par quelle mesure?
Prisonnier ZERO

3

Vous pouvez jeter un oeil à ce lien pour une comparaison des deux (et d'autres) approches dans un exemple réel.

Fondamentalement, lorsque les exigences changent, vous finissez par modifier plus de code si vous utilisez des usines au lieu de DI.

Ceci est également valable avec la DI manuelle (c'est-à-dire quand il n'y a pas de framework externe qui fournit les dépendances à vos objets, mais vous les passez dans chaque constructeur).


3

La plupart des réponses ici expliquent la différence conceptuelle et les détails de mise en œuvre des deux. Cependant, je n'ai pas pu trouver d'explication sur la différence d'application, laquelle IMO est la plus importante et l'OP a posé des questions. Alors laissez-moi rouvrir ce sujet ...

Une fois que quelqu'un m'a dit que c'est la façon dont vous l'utilisez qui fait la différence!

Exactement. Dans 90% des cas, vous pouvez obtenir une référence d'objet en utilisant Factory ou DI et vous vous retrouvez généralement avec ce dernier. Dans 10% des cas, utiliser Factory n'est que le bon moyen . Ces cas incluent l'obtention d'objets par variable aux paramètres d'exécution. Comme ça:

IWebClient client = factoryWithCache.GetWebClient(url: "stackoverflow.com",
        useCookies: false, connectionTimeout: 120);

Dans ce cas, obtenir clientde DI n'est pas possible (ou au moins nécessite une solution de contournement laide). Donc, en règle générale, pour prendre une décision: si une dépendance peut être obtenue sans aucun paramètre calculé lors de l'exécution, DI est préférable, sinon utilisez Factory.


2

je crois que DI est un moyen de configuration ou d'instanciation d'un bean. L'ID peut se faire de plusieurs manières comme constructeur, setter-getter etc.

Le modèle d'usine est juste une autre façon d'instancier des haricots. ce modèle sera utilisé principalement lorsque vous devrez créer des objets à l'aide du modèle de conception d'usine, car lorsque vous utilisez ce modèle, vous ne configurez pas les propriétés d'un bean, instanciez uniquement l'objet.

Vérifiez ce lien: Injection de dépendance


2

Binoj,

Je ne pense pas que vous deviez choisir l'un plutôt que l'autre.

Le fait de déplacer une classe ou une interface dépendante vers un constructeur ou un configurateur de classe suit le modèle DI. L'objet que vous passez au constructeur ou à l'ensemble peut être implémenté avec Factory.

Quand utiliser? Utilisez le ou les modèles qui se trouvent dans la timonerie de votre développeur. Avec quoi se sentent-ils le plus à l'aise et trouvent-ils le plus facile à comprendre.


2

Je crois que 3 aspects importants régissent les objets et leur utilisation:
1. Instanciation (d'une classe avec initialisation le cas échéant).
2. Injection (de l'instance ainsi créée) là où elle est requise.
3. Gestion du cycle de vie (de l'instance ainsi créée).

En utilisant le modèle Factory, le premier aspect (instanciation) est atteint mais les deux autres sont discutables. La classe qui utilise d'autres instances doit coder en dur les usines (au lieu de créer des instances), ce qui entrave les capacités de couplage lâches. De plus, la gestion du cycle de viedes instances devient un défi dans une grande application où une usine est utilisée à plusieurs endroits (en particulier, si l'usine ne gère pas le cycle de vie de l'instance qu'elle retourne, elle devient laide).

En utilisant un DI (de modèle IoC) d'autre part, tous les 3 sont extraits en dehors du code (dans le conteneur DI) et le bean géré n'a besoin de rien sur cette complexité. Couplage lâche , un objectif architectural très important peut être atteint confortablement au calme. Autre objectif architectural important, la séparation des préoccupations peut être réalisée bien mieux que les usines.

Alors que les usines peuvent convenir à de petites applications, les grandes préfèrent choisir DI plutôt que les usines.


1

Mes pensées:

Dependecy Injection: transmettez les paramètres des collaborateurs aux constructeurs. Dependency Injection Framework: une fabrique générique et configurable pour créer les objets à passer en paramètres aux constructeurs.


1
Oui, exactement. Presque toutes les mentions d'injection de dépendance (DI) dans ce Q&R abusent du terme contrairement à cette définition. Un conteneur d'injection de dépendance (DIC) est le cadre le plus courant agissant comme une fabrique générique et configurable pour créer des objets.
CXJ

1

Un cadre d'injection est une implémentation du modèle d'usine.

Tout dépend de vos besoins. Si vous avez besoin d'implémenter le modèle d'usine dans une application, il est fort probable que vos exigences seront satisfaites par l'une des innombrables implémentations du framework d'injection.

Vous ne devez déployer votre propre solution que si vos exigences ne peuvent être satisfaites par aucun des cadres tiers. Plus vous écrivez de code, plus vous devez coder. Le code est un passif et non un actif.

Les arguments sur la mise en œuvre que vous devez utiliser ne sont pas aussi importants que la compréhension des besoins architecturaux de votre application.


1

Modèle de conception d'usine

Le modèle de conception d'usine est caractérisé par

  • Une interface
  • Classes d'implémentation
  • Une usine

Vous pouvez observer peu de choses lorsque vous vous posez des questions comme ci-dessous

  • Quand l'usine créera-t-elle un objet pour les classes d'implémentation - exécution ou compilation?
  • Que faire si vous souhaitez changer d'implémentation au moment de l'exécution? - Pas possible

Celles-ci sont gérées par injection de dépendance.

Injection de dépendance

Vous pouvez avoir différentes manières d'injecter la dépendance. Pour plus de simplicité, allons-y avec l'Injection d'Injection

Dans DI, le conteneur crée les instances nécessaires et les "injecte" dans l'objet.

Élimine ainsi l'instanciation statique.

Exemple:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

1

D'une valeur nominale, ils se ressemblent

En termes très simples, Factory Pattern, un modèle de création aide à nous créer un objet - "Définir une interface pour créer un objet". Si nous avons une sorte de groupe d'objets de valeur clé (par exemple, un dictionnaire), en passant la clé à l'usine (je fais référence au modèle d'usine simple), vous pouvez résoudre le type. Travail accompli! Le Framework d'Injection de Dépendance (comme Structure Map, Ninject, Unity ... etc) d'autre part semble faire la même chose.

Mais ... "Ne réinventez pas la roue"

D'un point de vue architectural, c'est une couche contraignante et "Ne réinventez pas la roue".

Pour une application de niveau entreprise, le concept de DI est davantage une couche architecturale qui définit les dépendances. Pour simplifier davantage cela, vous pouvez le considérer comme un projet de bibliothèque de classes distinct, qui résout les dépendances. L'application principale dépend de ce projet où le résolveur de dépendances fait référence à d'autres implémentations concrètes et à la résolution de dépendances.

En plus de "GetType / Create" à partir d'une usine, le plus souvent, nous avons besoin de plus de fonctionnalités (possibilité d'utiliser XML pour définir les dépendances, la simulation et les tests unitaires, etc.). Puisque vous avez fait référence à Structure Map, consultez la liste des fonctionnalités Structure Map . C'est clairement plus qu'une simple résolution de mappage d'objet simple. Ne réinventez pas la roue!

Si vous n'avez qu'un marteau, tout ressemble à un clou

Selon vos besoins et le type d'application que vous créez, vous devez faire un choix. S'il n'a que peu de projets (peut-être un ou deux ..) et implique peu de dépendances, vous pouvez choisir une approche plus simple. C'est comme utiliser l'accès aux données ADO .Net plutôt que d'utiliser Entity Framework pour un simple appel à une ou deux bases de données, où l'introduction d'EF est une exagération dans ce scénario.

Mais pour un projet plus grand ou si votre projet s'agrandit, je recommanderais fortement d'avoir une couche DI avec un cadre et de faire de la place pour changer le cadre DI que vous utilisez (Utiliser une façade dans l'application principale (Web App, Web Api, Desktop ..etc.).


1

Avec une usine, vous pouvez regrouper les interfaces liées, donc si les paramètres passés peuvent être regroupés dans une usine, c'est également une bonne solution pour constructor overinjectionregarder ce code *):

public AddressModelFactory(IAddressAttributeService addressAttributeService,
        IAddressAttributeParser addressAttributeParser,
        ILocalizationService localizationService,
        IStateProvinceService stateProvinceService,
        IAddressAttributeFormatter addressAttributeFormatter)
    {
        this._addressAttributeService = addressAttributeService;
        this._addressAttributeParser = addressAttributeParser;
        this._localizationService = localizationService;
        this._stateProvinceService = stateProvinceService;
        this._addressAttributeFormatter = addressAttributeFormatter;
    }

Regardez le constructeur, il vous suffit de passer IAddressModelFactorylà, donc moins de paramètres *):

 public CustomerController(IAddressModelFactory addressModelFactory,
        ICustomerModelFactory customerModelFactory,
        IAuthenticationService authenticationService,
        DateTimeSettings dateTimeSettings,
        TaxSettings taxSettings,
        ILocalizationService localizationService,
        IWorkContext workContext,
        IStoreContext storeContext,
        ICustomerService customerService,
        ICustomerAttributeParser customerAttributeParser,
        ICustomerAttributeService customerAttributeService,
        IGenericAttributeService genericAttributeService,
        ICustomerRegistrationService customerRegistrationService,
        ITaxService taxService,
        CustomerSettings customerSettings,
        AddressSettings addressSettings,...

Vous voyez dans CustomerControllerbeaucoup de paramètres passés, oui vous pouvez le voir comme constructor overinjectionmais c'est comme ça que DI fonctionne. Et rien n'est faux avec le CustomerController.

*) Le code provient de nopCommerce.


1

En termes simples, la méthode de l'injection de dépendance par rapport à la méthode d'usine implique respectivement le mécanisme push vs pull.

Mécanisme Pull : la classe dépend indirectement de la méthode Factory qui à son tour dépend des classes concrètes.

Mécanisme de poussée : le composant racine peut être configuré avec tous les composants dépendants en un seul endroit et favorisant ainsi un entretien élevé et un couplage desserré.

Avec la méthode Factory, la responsabilité incombe toujours à la classe (bien qu'indirectement) de créer un nouvel objet alors que, comme pour l'injection de dépendance, cette responsabilité est externalisée (au prix d'une fuite d'abstraction)


0

Je pense que ceux-ci sont orthogonaux et peuvent être utilisés ensemble. Permettez-moi de vous montrer un exemple que j'ai récemment rencontré au travail:

Nous utilisions le framework Spring en Java pour DI. Une classe singleton ( Parent) devait instancier de nouveaux objets d'une autre classe ( Child), et ceux-ci avaient des collaborateurs complexes:

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

Dans cet exemple, Parentdoit recevoir des DepXinstances uniquement pour les transmettre au Childconstructeur. Problèmes avec ceci:

  1. Parenta plus de connaissances Childqu'il ne devrait
  2. Parent a plus de collaborateurs qu'il ne devrait
  3. Ajouter des dépendances à Childimplique de changerParent

C'est à ce moment-là que j'ai réalisé que ça Factoryirait parfaitement ici:

  1. Il masque tous les paramètres de la Childclasse sauf les vrais , comme leParent
  2. Il encapsule la connaissance de la création d'un Child, qui peut être centralisé dans la configuration DI.

Il s'agit de la Parentclasse simplifiée et de la ChildFactoryclasse:

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}

0

Vous utilisez l'injection de dépendances lorsque vous savez exactement de quel type d'objets vous avez besoin à ce stade. En cas de modèle d'usine, vous déléguez simplement le processus de création d'objets à l'usine car vous ne savez pas quel type exact d'objets vous avez besoin.


-1

J'utilise les deux pour créer une stratégie d'inversion de contrôle avec plus de lisibilité pour les développeurs qui ont besoin de la maintenir après moi.

J'utilise une Factory pour créer mes différents objets Layer (Business, Data Access).

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

Un autre développeur verra cela et lors de la création d'un objet de couche métier, il regarde dans BusinessFactory et Intellisense donne au développeur toutes les couches métier possibles à créer. N'a pas à jouer au jeu, trouvez l'interface que je veux créer.

Cette structure est déjà Inversion Of Control. Je ne suis plus responsable de la création de l'objet spécifique. Mais vous devez toujours vous assurer de l'injection de dépendances pour pouvoir changer les choses facilement. Créer votre propre injection de dépendance personnalisée est ridicule, donc j'utilise Unity. Dans CreateCarBusiness (), je demande à Unity de résoudre la classe qui appartient à cela et sa durée de vie.

Donc, ma structure d'injection de dépendances de code est:

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

Maintenant, j'ai l'avantage des deux. Mon code est également plus lisible pour les autres développeurs quant aux étendues de mes objets que j'utilise, au lieu de l'injection de dépendance de constructeur qui dit simplement que chaque objet est disponible lorsque la classe est créée.

J'utilise ceci pour changer mon accès aux données de base de données en une couche d'accès aux données codée personnalisée lorsque je crée des tests unitaires. Je ne veux pas que mes tests unitaires communiquent avec des bases de données, des serveurs Web, des serveurs de messagerie, etc. Ils doivent tester ma couche métier, car c'est là que réside l'intelligence.


-4

Utiliser l'injection de dépendances est beaucoup mieux à mon avis si vous êtes: 1. déployant votre code dans une petite partition, car il gère bien le découplage d'un gros code. 2. la testabilité est l'un des cas où DI est correct à utiliser car vous pouvez vous moquer facilement des objets non découplés. avec l'utilisation d'interfaces, vous pouvez facilement simuler et tester chaque objet. 3. vous pouvez simultanément réviser chaque partie du programme sans avoir besoin de coder l'autre partie depuis son découplage lâche.

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.