L'Injection de dépendances doit-elle se faire aux dépens de l'Encapsulation?

si je comprends bien, le mécanisme typique pour L'Injection de dépendance est de l'injecter soit par l'intermédiaire d'un constructeur de classe, soit par l'intermédiaire d'une propriété publique (membre) de la classe.

cela expose la dépendance injectée et viole le principe D'encapsulation OOP.

ai-je raison d'identifier ce compromis? Comment abordez-vous cette question?

voir aussi ma réponse à ma propre question dessous.

115
demandé sur urig 2009-06-17 10:58:01

21 réponses

il y a une autre façon d'aborder cette question qui pourrait vous intéresser.

lorsque nous utilisons L'injection IoC/dépendance, nous n'utilisons pas les concepts OOP. Certes, nous utilisons un langage OO comme "hôte", mais les idées qui sous-tendent IoC proviennent de l'ingénierie logicielle axée sur les composants, et non de L'OO.

logiciel de composant est tout au sujet de la gestion des dépendances - un exemple dans l'usage courant est. le mécanisme D'assemblage de .NET. Chaque assemblée publie la liste des les assemblages qu'il fait référence, et cela rend beaucoup plus facile de rassembler (et valider) les pièces nécessaires pour une application en cours d'exécution.

en appliquant des techniques similaires dans nos programmes OO via IoC, nous cherchons à rendre les programmes plus faciles à configurer et à maintenir. Les dépendances de publication (en tant que paramètres du constructeur ou autre) en sont un élément clé. L'Encapsulation ne s'applique pas vraiment, car dans le monde axé sur les composants / Services, il n'y a pas de "type d'implémentation" pour les détails fuite d'.

Malheureusement, nos langues ne séparent pas actuellement les concepts à grains fins et orientés objet des concepts à grains grossiers orientés composants, de sorte que c'est une distinction que vous devez garder dans votre esprit seulement:)

58
répondu Nicholas Blumhardt 2009-11-26 00:11:00

c'est une bonne question - mais à un certain point, l'encapsulation dans sa forme la plus pure a besoin pour être violée si l'objet doit jamais avoir sa dépendance accomplie. Certains fournisseurs de la dépendance doit savent à la fois que l'objet en question nécessite un Foo , et le fournisseur doit avoir un moyen de fournir le Foo à l'objet.

classiquement, ce dernier cas est traité comme vous le dites, par arguments de constructeur ou méthodes de setter. Cependant, ce n'est pas nécessairement vrai - je sais que les dernières versions du cadre Di De Spring en Java, par exemple, vous permettent d'annoter des champs privés (par exemple avec @Autowired ) et la dépendance sera définie via la réflexion sans que vous ayez besoin d'exposer la dépendance à travers l'une des classes public methods/constructors. Cela pourrait être le type de solution que vous recherchez.

cela dit, Je ne pense pas que le constructeur l'injection est un gros problème non plus. J'ai toujours pensé que les objets devraient être entièrement valides après la construction, de sorte que tout ce dont ils ont besoin pour remplir leur rôle (c.-à-d. être dans un état valide) devrait être fourni par le constructeur de toute façon. Si vous avez un objet qui nécessite un collaborateur pour travailler, il semble bien que le constructeur annonce publiquement cette exigence et s'assure qu'elle est remplie lorsqu'une nouvelle instance de la classe est créée.

Idéalement, lorsque vous avez affaire à des objets, vous interagissez avec eux via une interface de toute façon, et plus vous le faites (et avez des dépendances câblées Via DI), moins vous avez réellement à traiter avec les constructeurs vous-même. Dans la situation idéale, votre code ne traite pas ou même ne crée jamais d'instances concrètes de classes; donc il obtient juste un IFoo par DI, sans se soucier de ce que le constructeur de FooImpl indique qu'il a besoin de faire son travail, et en fait sans même être je suis au courant de l'existence de FooImpl . De ce point de vue, l'encapsulation est parfait.

c'est une opinion bien sûr, mais à mon avis DI ne viole pas nécessairement encapsulation et en fait peut l'aider en centralisant toutes les connaissances nécessaires des internes en un seul endroit. Non seulement c'est une bonne chose en soi, mais encore mieux cet endroit est en dehors de votre propre base de code, donc aucun du code que vous écrivez n'a besoin de connaître les dépendances des classes.

28
répondu Andrzej Doyle 2009-06-17 11:17:03

cela expose la dépendance injectée et viole le principe D'encapsulation OOP.

Eh bien, franchement parlant, tout viole l'encapsulation. :) C'est une sorte de principe de soumission qui doit être bien traité.

alors, qu'est-ce qui viole l'encapsulation?

Héritage ne .

" parce que l'héritage expose un sous-classe aux détails de la mise en œuvre de ses parents, il est souvent dit que 'l'héritage casse encapsulation'". (Gang of Four 1995: 19)

Aspect-oriented programming does . Par exemple, vous vous enregistrez sur le callback de methodcall() et cela vous donne une excellente occasion d'injecter du code dans l'évaluation de la méthode normale, en ajoutant des effets secondaires étranges, etc.

ami déclaration en C++ ne .

de la Classe de l'extension en Ruby ne . Il suffit de redéfinir une méthode string quelque part après qu'une classe string ait été entièrement définie.

Eh bien, beaucoup de choses fait .

Encapsulation est un bon principe important. Mais pas le seul.

switch (principle)
{
      case encapsulation:
           if (there_is_a_reason)
      break!
}
15
répondu topright gamedev 2010-04-25 18:54:05

Oui, DI viole l'encapsulation (aussi connu comme "se cacher de l'information").

mais le vrai problème vient quand les développeurs l'utilisent comme excuse pour violer les principes KISS (Keep It Short and Simple) et YAGNI (You Ain't Gonna Need It).

personnellement, je préfère des solutions simples et efficaces. J'utilise principalement le "nouvel" opérateur pour instancier les dépendances stateful quand et où elles sont nécessaires. C'est simple, bien encapsulé, facile à comprendre et facile à tester. Alors, pourquoi pas?

11
répondu Rogério 2009-07-21 23:10:34

ça ne viole pas l'encapsulation. Vous fournissez un collaborateur, mais la classe décide comment il est utilisé. Tant que vous suivez Dire de ne pas demander les choses sont très bien. Je trouve constructeur injection préférable, mais setters peut être très bien aussi longtemps qu'ils sont intelligents. C'est qu'ils contiennent de la logique de maintenir les invariants de la classe représente.

5
répondu Jason Watkins 2009-06-17 07:57:42

un bon contenant/système d'injection de depenancy permettra l'injection par le constructeur. Les objets dépendants seront encapsulés, et n'ont pas besoin d'être exposés publiquement du tout. De plus, en utilisant un système DP, aucun de vos codes ne "connaît" les détails de la construction de l'objet, peut-être même en incluant l'objet en cours de construction. Il y a plus d'encapsulation dans ce cas puisque presque tout votre code non seulement est protégé de la connaissance des objets encapsulés, mais ne participer à la construction des objets.

maintenant, je suppose que vous comparez avec le cas où l'objet créé crée ses propres objets encapsulés, très probablement dans son constructeur. D'après ce que j'ai compris de DP, nous voulons retirer cette responsabilité de l'objet et la confier à quelqu'un d'autre. À cette fin, le "quelqu'un d'autre", qui est le DP conteneur dans ce cas, n'ont qu'une connaissance intime qui "viole" encapsulation; l'avantage est qu'il tire cette connaissance hors de l'objet, c'est moi. Quelqu'un doit l'avoir. Le reste de votre demande ne l'est pas.

je penserais à cela de cette façon: le conteneur/système d'injection de dépendance viole l'encapsulation, mais votre code ne le fait pas. En fait, votre code est plus "encapsulé" que jamais.

5
répondu Bill 2009-06-17 10:49:09

l'encapsulation Pure est un idéal qui ne peut jamais être atteint. Si toutes les dépendances étaient cachées, vous n'auriez pas du tout besoin de DI. Pensez-y de cette façon, si vous avez vraiment des valeurs privées qui peuvent être internalisées dans l'objet, disons par exemple la valeur entière de la vitesse d'un objet de voiture, alors vous n'avez pas de dépendance externe et pas besoin d'inverser ou d'injecter cette dépendance. Ces types de valeurs d'état internes qui sont exploités sur purement par des fonctions privées sont ce que vous voulez pour encapsuler toujours.

mais si vous construisez une voiture qui veut un certain type d'objet moteur alors vous avez une dépendance externe. Vous pouvez soit instancier ce moteur-par exemple la nouvelle GMOverHeadCamEngine () - en interne dans le constructeur de l'objet de voiture, en préservant l'encapsulation mais en créant un couplage beaucoup plus insidieux à une GMOverHeadCamEngine de classe de béton, ou vous pouvez l'injecter, permettant à votre objet de voiture de fonctionner agnostiquement (et beaucoup plus plus robustement) sur par exemple une interface IEngine sans la dépendance concrète. Que vous utilisiez un conteneur IOC ou un simple DI pour y parvenir, ce n'est pas la question -- le fait est que vous avez une voiture qui peut utiliser de nombreux types de moteurs sans être couplé à l'un d'entre eux, ce qui rend votre codebase plus flexible et moins sujette aux effets secondaires.

DI n'est pas une violation de l'encapsulation, c'est un moyen de minimiser l'accouplement quand l'encapsulation est nécessairement cassé comme une question de cours dans pratiquement chaque projet OOP. Injecter une dépendance dans une interface extérieurement minimise les effets secondaires du couplage et permet à vos classes de rester agnostiques quant à l'implémentation.

3
répondu Dave Sims 2010-01-14 19:12:40

c'est similaire à la réponse en votation, mais je veux penser à haute voix - peut-être que d'autres voient les choses de cette façon aussi.

  • Classique OO utilise des constructeurs pour définir le public "initialisation" contrats pour les consommateurs de la classe (en cachant TOUS les détails de mise en œuvre; aka encapsulation). Ce Contrat peut assurer qu'après instanciation vous avez un objet prêt à l'emploi (i.e. pas d'étapes d'initialisation supplémentaires à retenir (er, oublié) par utilisateur.)

  • (constructeur) di casse indéniablement encapsulation par saignement détail de mise en œuvre à travers cette interface public constructeur. Aussi longtemps que nous considérons encore le constructeur public responsable de définir le contrat d'initialisation pour les utilisateurs, nous avons créé une horrible violation de l'encapsulation.

Exemple Théorique:

Classe Foo a 4 méthodes et a besoin d'un entier pour l'initialisation, de sorte que son constructeur ressemble à Foo(taille int) et il est immédiatement clair pour les utilisateurs de la classe Foo qu'ils doivent fournir une taille instanciation afin de Foo à travailler.

dire que cette mise en œuvre particulière de Foo peut également avoir besoin d'un IWidget pour faire son travail. Injection du constructeur cette dépendance nous ferait créer un constructeur comme Foo (taille int, widget IWidget)

ce qui me dérange à ce sujet est maintenant nous avons un constructeur qui est mélange données d'initialisation avec des dépendances - une entrée est d'intérêt pour l'utilisateur de la classe ( taille ), l'autre est une dépendance interne qui ne sert à confondre l'utilisateur et est un détail de mise en œuvre ( widget ).

le paramètre Taille n'est pas une dépendance - c'est simplement une valeur d'initialisation par instance. IoC est parfait pour les dépendances externes (comme widget) mais pas pour l'initialisation d'état interne.

encore pire, que faire si le Widget n'est nécessaire que pour 2 des 4 Méthodes sur cette classe; je peux être encurrer instanciation overhead pour Widget même s'il ne peut pas être utilisé!

Comment compromis/concilier cela?

une approche consiste à passer exclusivement aux interfaces pour définir le contrat d'exploitation; et supprimer l'utilisation de constructeurs par les utilisateurs. Pour être cohérent, tous les objets devraient être accessibles uniquement par des interfaces, et instanciés uniquement par une certaine forme de résolveur (comme un conteneur IOC/DI). Seul le conteneur peut instancier des choses.

qui prend soin de la dépendance du Widget, mais comment initialiser la "taille" sans recourir à un méthode d'initialisation séparée sur L'interface Foo? En utilisant cette solution, nous avons perdu la capacité de nous assurer qu'une instance de Foo est entièrement initialisée au moment où vous obtenez l'instance. Dommage, parce que j'aime vraiment le idée et simplicité de l'injection de constructeur.

comment atteindre l'initialisation garantie dans ce monde DI, quand l'initialisation est plus que seulement des dépendances externes?

3
répondu shawnT 2010-02-25 06:16:09

comme Jeff Sternal l'a souligné dans un commentaire à la question, la réponse dépend entièrement de la façon dont vous définissez encapsulation .

il semble y avoir deux camps principaux de ce que signifie encapsulation:

  1. Tout ce qui concerne l'objet est une méthode sur un objet. Ainsi, un objet File peut avoir des méthodes pour Save , Print , Display , ModifyText , etc.
  2. Un objet est son propre petit monde, et ne dépend pas d'un comportement extérieur.

ces deux définitions sont en contradiction directe. Si un objet File peut s'imprimer lui-même, il dépendra fortement du comportement de l'imprimante. D'un autre côté, s'il ne s'agit que de sait à propos de quelque chose qui peut être imprimé pour lui (une IFilePrinter ou une interface de ce type), alors l'objet File n'a pas besoin de savoir quoi que ce soit l'impression, et donc le travail avec elle apportera moins de dépendances dans l'objet.

ainsi, l'injection de dépendance cassera l'encapsulation si vous utilisez la première définition. Mais, franchement, je ne sais pas si j'aime la première définition - elle n'a clairement pas d'échelle (si c'était le cas, MS Word serait une grande classe).

, d'autre part, l'injection de dépendance est presque obligatoire si vous utilisez la deuxième définition de l'encapsulation.

3
répondu kyoryu 2010-04-25 19:12:29

cela dépend si la dépendance est vraiment un détail d'implémentation ou quelque chose que le client voudrait/devrait savoir d'une manière ou d'une autre. Une chose qui est pertinente est le niveau d'abstraction que la classe vise. Voici quelques exemples:

si vous avez une méthode qui utilise la mise en cache sous le capot pour accélérer les appels, alors l'objet cache doit être un Singleton ou quelque chose et doit être injecté et non . Fait le fait que le cache soit utilisé est un détail d'implémentation dont les clients de votre classe ne devraient pas se soucier.

si votre classe a besoin de produire des flux de données, il est probablement logique d'injecter le flux de sortie de sorte que la classe puisse facilement produire les résultats à un tableau, un fichier, ou n'importe où ailleurs quelqu'un d'autre pourrait vouloir envoyer les données.

pour une zone grise, disons que vous avez une classe qui fait une simulation de monte carlo. Il besoin d'une source de hasard. D'une part, le fait qu'il a besoin de cela est un détail de mise en œuvre dans le sens où le client ne se soucie pas vraiment d'où vient le hasard. D'autre part, puisque les générateurs de nombres aléatoires du monde réel font des compromis entre le degré d'aléatoire, la vitesse, etc. pour que le client puisse vouloir contrôler, et pour que le client puisse vouloir contrôler le semis pour obtenir un comportement reproductible, l'injection peut avoir du sens. Dans ce cas, je suggère d'offrir un moyen de créer classe sans spécifier un générateur de nombres aléatoires, et utiliser un thread-local Singleton comme valeur par défaut. Si/quand le besoin d'un contrôle plus fin se présente, fournir un autre constructeur qui permet une source d'aléatoire à injecter.

2
répondu dsimcha 2010-01-27 03:33:31
L'Encapsulation

n'est rompue que si une classe A à la fois la responsabilité de créer l'objet (ce qui exige la connaissance des détails de mise en œuvre) et utilise ensuite la classe (ce qui n'exige pas la connaissance de ces détails). Je vais vous expliquer pourquoi, mais d'abord un rapide car anaology:

quand je conduisais mon vieux kombi 1971, Je pouvais appuyer sur l'accélérateur et il suis allé (un peu) plus rapide. Je n'ai pas besoin de savoir pourquoi, mais les gars qui construit le Kombi à l'usine savait exactement pourquoi.

mais retour au codage. Encapsulation cache un détail d'implémentation de quelque chose qui utilise cette implémentation."L'Encapsulation est une bonne chose parce que les détails de l'implémentation peuvent changer sans que l'utilisateur de la classe le sache.

Lorsqu'on utilise l'injection de dépendances, l'injection de constructeur est utilisée pour construire des objets de type service (par opposition à à l'entité/des objets de valeur de modèle de l'état). Toutes les variables de membre dans l'objet de type de service représentent des détails de mise en œuvre qui ne devraient pas être divulgués. par exemple, le numéro de port de la socket, les justificatifs d'identité de la base de données, une autre classe à appeler pour effectuer le cryptage, un cache, etc.

le constructeur est pertinent lorsque la classe est créée initialement. Cela se produit pendant la phase de construction pendant que votre Di container (ou usine) fils ensemble tous les objets de service. Le conteneur DI ne connaît que les détails de la mise en œuvre. Il sait tout sur les détails de mise en œuvre comme les gars à L'usine de Kombi savent sur les bougies.

à l'exécution, l'objet de service qui a été créé est appelé apon pour faire un travail réel. À ce moment, l'appelant de l'objet ne sait rien des détails de mise en œuvre.

C'est moi conduisant mon Kombi à la plage.

maintenant, retour à encapsulation. Si les détails de l'implémentation changent, alors la classe utilisant cette implémentation à l'exécution n'a pas besoin de changer. L'Encapsulation n'est pas cassée.

je peux conduire ma nouvelle voiture pour la plage aussi. L'Encapsulation n'est pas cassée.

si les détails de la mise en œuvre changent, le conteneur DI (ou l'usine) doit changer. Vous n'avez jamais essayé de cacher les détails de la mise en œuvre à l'usine en premier lieu.

2
répondu WW. 2011-07-07 02:52:13

ayant lutté avec la question un peu plus loin, je suis maintenant dans l'opinion que L'Injection de dépendance ne viole (à ce moment-ci) encapsulation dans une certaine mesure. Ne vous méprenez pas cependant sur moi - je pense que l'injection de dépendance vaut le détour dans la plupart des cas.

la raison pour laquelle L'ai viole l'encapsulation devient claire lorsque le composant sur lequel vous travaillez doit être livré à une partie" externe " (pensez à écrire une bibliothèque pour un client).

quand mon composant nécessite que des sous-composants soient injectés via le constructeur (ou les propriétés publiques) il n'y a pas de garantie pour

"empêcher les utilisateurs de régler les données internes du composant dans un état invalide ou incohérent".

en même temps on ne peut pas dire que

" utilisateurs du composant (autres pièces de logiciel) ont seulement besoin de savoir ce que le composant fait, et ne peuvent pas se rendre dépendant des détails de la façon dont il le fait" .

les deux citations sont de wikipedia .

pour donner un exemple précis: je dois livrer une DLL côté client qui simplifie et dissimule la communication à un service de la WCF (essentiellement une façade distante). Parce que cela dépend de 3 classes de proxy différentes de la WCF, si je prends la DI approche, je suis forcé de les exposer via le constructeur. Avec cela j'expose les internes de ma couche de communication que j'essaye de cacher.

en général, je suis entièrement pour DI. Dans cet exemple (extrême) particulier, il me semble dangereux.

2
répondu urig 2017-02-23 09:31:06

j'ai lutté avec cette notion aussi. Au début, l '"exigence" d'utiliser le di container (comme le ressort) pour instancier un objet ressenti comme sautant à travers des cerceaux. Mais en réalité, ce n'est pas vraiment un cerceau - c'est juste une autre façon "publiée" de créer des objets dont j'ai besoin. Bien sûr, l'encapsulation est "cassée" parce que quelqu'un "en dehors de la classe" sait ce dont elle a besoin, mais ce n'est pas vraiment le reste du système qui le sait - c'est le conteneur DI. Rien de magique ne se passe autrement car DI "sait" qu'un objet en a besoin d'un autre.

en fait, C'est encore mieux - en se concentrant sur les usines et les dépôts Je n'ai même pas besoin de savoir que DI est impliqué du tout! Pour moi, ça remet le couvercle sur l'encapsulation. Ouf!

1
répondu n8wrl 2009-06-25 20:02:23

je crois en la simplicité. L'application de L'Injection IOC/Dependecy dans les classes de domaines n'apporte aucune amélioration, sauf à rendre le code beaucoup plus difficile à gérer en disposant d'un fichier xml externe décrivant la relation. De nombreuses technologies comme EJB 1.0 / 2.0 & struts 1.1 sont inversées en réduisant le matériel mis en XML et essayer de les mettre en code comme agacement, etc. Donc, appliquer le CIO pour toutes les classes que vous développez rendra le code illogique.

avantages pour le CIO lorsque l'objet dépendant n'est pas prête pour la création au moment de la compilation. Cela peut se produire dans la plupart des composants de l'architecture abstraite à niveau d'Infrastructure, En essayant d'établir un cadre de base commun qui pourrait devoir fonctionner pour différents scénarios. Dans ces endroits, L'usage du CIO est plus logique. Cela ne rend pas le code plus simple / maintenable.

comme toutes les autres technologies, cela aussi a des avantages et des inconvénients. Mon souci est, nous mettons en œuvre les dernières technologies dans tous les endroits indépendamment de leur meilleure utilisation de contexte.

1
répondu Senthil Kumar Vaithilingam 2009-11-24 19:56:36

PS. En fournissant l'Injection de Dépendance vous ne sont pas nécessairement pause Encapsulation . Exemple:

obj.inject_dependency(  factory.get_instance_of_unknown_class(x)  );
Le code Client

ne connaît pas encore les détails de la mise en œuvre.

1
répondu topright gamedev 2010-04-25 19:51:37

peut-être que c'est une façon naïve d'y penser, mais quelle est la différence entre un constructeur qui prend un paramètre entier et un constructeur qui prend un service comme paramètre? Cela signifie-t-il que définir un entier en dehors du nouvel objet et l'introduire dans l'objet rompt l'encapsulation? Si le service est utilisé uniquement dans le nouvel objet, je ne vois pas comment cela pourrait briser l'encapsulation.

aussi, en utilisant une sorte de fonction d'auto-apprentissage (Autofac pour C#, par exemple), il rend le code extrêmement propre. En construisant des méthodes d'extension pour le constructeur Autofac, j'ai été capable de découper beaucoup de code de configuration DI que j'aurais dû maintenir au fil du temps alors que la liste des dépendances augmentait.

1
répondu blooware 2010-06-04 17:11:04

je suis d'accord que pris à l'extrême, DI peut violer l'encapsulation. Habituellement, DI expose des dépendances qui n'ont jamais été vraiment encapsulées. Voici un exemple simplifié emprunté à Miško Hevery les Singleton sont des menteurs pathologiques :

vous commencez par un test de carte de crédit et écrivez un test unitaire simple.

@Test
public void creditCard_Charge()
{
    CreditCard c = new CreditCard("1234 5678 9012 3456", 5, 2008);
    c.charge(100);
}

le mois prochain, vous aurez une facture de 100$. Pourquoi vous êtes-vous chargé? L'essai à l'unité a affecté une production la base de données. À l'interne, les appels de carte de crédit Database.getInstance() . Refacturation carte de crédit de sorte qu'il prend un DatabaseInterface dans son constructeur expose le fait qu'il ya une dépendance. Mais je dirais que la dépendance n'a jamais été encapsulée au départ puisque la classe de la carte de crédit provoque des effets secondaires visibles de l'extérieur. Si vous voulez tester une carte de crédit sans la modifier, vous pouvez certainement observer la dépendance.

@Before
public void setUp()
{
    Database.setInstance(new MockDatabase());
}

@After
public void tearDown()
{
    Database.resetInstance();
}

ça ne vaut pas la peine de se demander si exposer la base de données comme une dépendance réduit l'encapsulation, parce que c'est une bonne conception. Toutes les décisions de L'AI ne seront pas aussi simples. Cependant, aucune des autres réponses montrent un contre-exemple.

0
répondu Craig P. Motlin 2010-07-20 14:24:23

je pense que c'est une question de portée. Lorsque vous définissez encapsulation (ne pas laisser savoir comment), vous devez définir ce qui est la fonctionnalité encapsulée.

  1. Classe as est : ce que vous encapsulez est la seule responsabilité de la classe. Ce qu'il sait le faire. Par exemple, le tri. Si vous injectez un comparateur pour commander, disons, des clients, cela ne fait pas partie de la chose encapsulée: quicksort.

  2. fonctionnalité configurée : si vous voulez fournir une fonctionnalité prête à l'emploi, alors vous ne fournissez pas la classe QuickSort, mais une instance de classe QuickSort configurée avec un comparateur. Dans ce cas, le code responsable de la création et de la configuration qui doit être caché du code utilisateur. Et c'est l'encapsulation.

lorsque vous programmez des classes, il est, la mise en œuvre responsabilités uniques en classe, vous utilisez l'option 1.

quand vous programmez des applications, il est, faire quelque chose qui entreprend quelque chose d'utile béton travailler alors vous êtes repeteadily en utilisant l'option 2.

c'est l'implémentation de l'instance configurée:

<bean id="clientSorter" class="QuickSort">
   <property name="comparator">
      <bean class="ClientComparator"/>
   </property>
</bean>

voici comment un autre code client l'utilise:

<bean id="clientService" class"...">
   <property name="sorter" ref="clientSorter"/>
</bean>

il est encapsulé parce que si vous changez l'implémentation (vous changez la définition de clientSorter bean), cela ne casse pas l'utilisation du client. Peut-être, comme vous utilisez des fichiers xml avec tous écrits ensemble, vous voyez tous les détails. Mais croyez-moi, le code client ( ClientService ) ne sait pas rien à propos de son trieur.

0
répondu helios 2012-08-09 07:39:58

il est probablement intéressant de mentionner que Encapsulation est quelque peu dépendant de la perspective.

public class A { 
    private B b;

    public A() {
        this.b = new B();
    }
}


public class A { 
    private B b;

    public A(B b) {
        this.b = b;
    }
}

du point de vue de quelqu'un qui travaille sur la classe A , dans le deuxième exemple A sait beaucoup moins sur la nature de this.b

considérant que, sans DI

new A()

vs

new A(new B())

La personne qui regarde ce code en sait plus sur la nature de A dans le deuxième exemple.

avec DI, au moins tout ce que la connaissance divulguée est dans un endroit.

0
répondu Tom B 2015-05-26 12:53:02

je pense qu'il est évident qu'à tout le moins DI affaiblit considérablement l'encapsulation. En plus de cela voici quelques autres inconvénients de DI à considérer.

  1. il rend le code plus difficile à réutiliser. Un module qu'un client peut utiliser sans avoir à fournir explicitement des dépendances est évidemment plus facile à utiliser qu'un module où le client doit d'une façon ou d'une autre découvrir quelles sont les dépendances de ce composant et ensuite les rendre disponibles. Par exemple, un composant créé à l'origine pour être utilisé dans une application ASP peut s'attendre à ce que ses dépendances soient fournies par un conteneur DI qui fournit aux instances d'objet des durées de vie liées aux requêtes http du client. Cela peut ne pas être simple à reproduire dans un autre client qui ne vient pas avec le même conteneur DI construit que L'application D'origine ASP.

  2. il peut rendre le code plus fragile. Les dépendances fournies par la spécification de l'interface peuvent être implémenté de manière inattendue, ce qui donne lieu à toute une classe de bogues d'exécution qui ne sont pas possibles avec une dépendance concrète résolue statiquement.

  3. il peut rendre le code moins flexible dans le sens où vous pouvez finir avec moins de choix sur la façon dont vous voulez qu'il fonctionne. Toutes les classes n'ont pas besoin d'avoir toutes leurs dépendances en existence pour toute la durée de vie de l'instance propriétaire, mais avec de nombreuses implémentations DI vous n'avez pas d'autre option.

dans cet esprit, je pense que la question la plus importante devient alors, " est-ce qu'une dépendance particulière doit être spécifiée extérieurement? ". Dans la pratique, j'ai rarement trouvé nécessaire de faire une dépendance fournie à l'extérieur juste pour supporter le test.

Lorsqu'une dépendance doit réellement être fournie de l'extérieur, cela suggère normalement que la relation entre les objets est la collaboration plutôt qu'une dépendance interne, auquel cas le but approprié est alors l'encapsulation de chaque classe , plutôt que l'encapsulation d'une classe à l'intérieur de l'autre.

d'après mon expérience, le principal problème concernant L'utilisation de DI est que si vous commencez avec un cadre d'application avec DI intégré, ou si vous ajoutez DI support à votre base de données, pour une raison quelconque, les gens supposent que depuis que vous avez di support qui doit être la bonne façon de instancier tout . Ils ne prennent même jamais la peine de se poser la question: "cette dépendance doit-elle être spécifiée de l'extérieur?". Et pire, ils commencent aussi à essayer de forcer tout le monde à utiliser le support DI pour tout aussi.

le résultat de ceci est que inexorablement votre codebase commence à passer dans un état où la création de n'importe quelle instance de n'importe quoi dans votre codebase nécessite des rames de conteneur obtuse DI configuration, et déboguer quoi que ce soit est deux fois plus difficile parce que vous avez la charge de travail supplémentaire d'essayer d'identifier comment et où quelque chose a été instancié.

voici donc ma réponse à la question. utilisez DI où vous pouvez identifier un problème réel qu'il résout pour vous, que vous ne pouvez pas résoudre plus simplement d'une autre manière.

0
répondu Neutrino 2017-10-27 09:11:39

DI viole d'Encapsulation pour les NON-objets Partagés de la période. Les objets partagés ont une durée de vie en dehors de l'objet créé, et doivent donc être agrégés dans l'objet créé. Les objets qui sont privés de l'objet créé doit être COMPOSÉ dans l'objet créé - lorsque l'objet est détruit, il se compose d'objet. Prenons le corps humain comme un exemple. Ce qui est composé et ce qui est agrégé. Si nous devions utiliser DI, le corps humain le constructeur aurait des centaines d'objets. De nombreux organes, par exemple, sont (potentiellement) remplaçable. Mais, ils sont toujours composés dans le corps. Les cellules sanguines sont créées dans le corps (et détruites) tous les jours, sans besoin d'influences externes (autres que les protéines). Ainsi, les cellules sanguines sont créées à l'intérieur par le corps - nouvelle cellule de sang().

de Militants de DI affirment qu'un objet ne doivent JAMAIS utiliser l'opérateur new. Cette approche "puriste" non seulement viole encapsulation mais aussi le principe de substitution de Liskov pour celui qui crée l'objet.

0
répondu Greg Brand 2018-03-22 19:16:06