Multitudes construisant une implémentation. DI sans espoir? Utiliser le localisateur de services?


14

Supposons que 1001 clients construisent directement leurs dépendances plutôt que d'accepter les injections. La refactorisation du 1001 n'est pas une option selon notre patron. En fait, nous ne sommes même pas autorisés à accéder à leur source, uniquement aux fichiers de classe.

Ce que nous sommes censés faire, c'est «moderniser» le système que ces 1001 clients traversent. Nous pouvons refactoriser tout ce que nous aimons. Les dépendances font partie de ce système. Et certaines de ces dépendances que nous sommes censés changer pour avoir une nouvelle implémentation.

Ce que nous aimerions faire, c'est avoir la possibilité de configurer différentes implémentations de dépendances pour satisfaire cette multitude de clients. Malheureusement, DI ne semble pas être une option car les clients n'acceptent pas les injections de constructeurs ou de poseurs.

Les options:

1) Refactoriser la mise en œuvre du service que les clients utilisent pour qu'il fasse ce dont les clients ont besoin maintenant. Bang, nous avons terminé. Pas flexible. Pas complexe.

2) Refactorisez l'implémentation afin qu'elle délègue son travail à une autre dépendance qu'elle acquiert via une usine. Nous pouvons maintenant contrôler quelle implémentation ils utilisent tous en refactorisant l'usine.

3) Refactorisez l'implémentation afin qu'elle délègue son travail à une autre dépendance qu'elle acquiert via un localisateur de services. Maintenant, nous pouvons contrôler quelle implémentation ils utilisent tous en configurant le localisateur de service qui pourrait simplement être une hashmapchaîne de caractères vers des objets avec un petit casting en cours.

4) Quelque chose auquel je n'ai même pas encore pensé.

L'objectif:

Minimisez les dommages causés à la conception en faisant glisser l'ancien code client mal conçu vers l'avenir sans ajouter de complexité inutile.

Les clients ne doivent pas connaître ou contrôler l'implémentation de leurs dépendances mais ils insistent pour les construire avec new. Nous ne pouvons pas contrôler le newmais nous contrôlons la classe qu'ils construisent.

Ma question:

Qu'est-ce que j'ai omis de considérer?


Questions de Doc Brown

avez-vous vraiment besoin d'une possibilité de configuration entre différentes implémentations? Dans quel but?

Agilité. Beaucoup d'inconnues. La direction souhaite un potentiel de changement. Ne perdez votre dépendance vis-à-vis du monde extérieur. Test également.

avez-vous besoin d'une mécanique d'exécution ou simplement d'une mécanique de compilation pour basculer entre différentes implémentations? Pourquoi?

La mécanique du temps de compilation est probablement suffisante. Sauf pour les tests.

De quelle granularité avez-vous besoin pour basculer entre les implémentations? Tout à la fois? Par module (contenant chacun un groupe de classes)? Par classe?

Sur les 1001, un seul est exécuté à travers le système à la fois. Changer ce que tous les clients utilisent en même temps est très bien. Le contrôle individuel des dépendances est cependant probablement important.

qui doit contrôler l'interrupteur? Uniquement votre / votre équipe de développeurs? Un administrateur? Chaque client seul? Ou les développeurs de maintenance pour le code du client? Alors, comment la mécanique doit-elle être facile / robuste / infaillible?

Dev pour les tests. Administrateur lorsque les dépendances matérielles externes changent. Il doit être facile à tester et à configurer.


Notre objectif est de montrer que le système peut être refait rapidement et modernisé.


cas d'utilisation réel du commutateur d'implémentation?

Premièrement, certaines données seront fournies par le logiciel jusqu'à ce que la solution matérielle soit prête.


1
Je ne pense pas que vous réussirez beaucoup en stockant des usines ou des localisateurs dans un état mondial. Pourquoi s'embêter? Allez-vous tester les clients en remplaçant la dépendance?
Basilevs

Pensez à utiliser un chargeur de classe personnalisé.
Basilevs

Par curiosité, que fait ce système?
Prime

Réponses:


7

Eh bien, je ne suis pas sûr de bien comprendre les détails techniques et leurs différences exactes de vos solutions prises en charge, mais à mon humble avis, vous devez d'abord savoir de quel type de flexibilité vous avez vraiment besoin.

Les questions que vous devez vous poser sont:

  • avez-vous vraiment besoin d'une possibilité de configuration entre différentes implémentations? Dans quel but?

  • avez-vous besoin d'une mécanique d'exécution ou simplement d'une mécanique de compilation pour basculer entre différentes implémentations? Pourquoi?

  • De quelle granularité avez-vous besoin pour basculer entre les implémentations? Tout à la fois? Par module (contenant chacun un groupe de classes)? Par classe?

  • qui doit contrôler l'interrupteur? Uniquement votre / votre équipe de développeurs? Un administrateur? Chaque client seul? Ou les développeurs de maintenance pour le code du client? Alors, comment la mécanique doit-elle être facile / robuste / infaillible?

Ayant à l'esprit les réponses à ces questions, choisissez la solution la plus simple à laquelle vous pouvez penser qui vous offre la flexibilité requise. N'implémentez aucune flexibilité dont vous n'êtes pas sûr "au cas où" si cela signifie un effort supplémentaire.

En réponse à vos réponses: si vous avez au moins un cas d'utilisation réel à portée de main, utilisez-le pour valider vos décisions de conception. Utilisez ce cas d'utilisation pour savoir quel type de solution vous convient le mieux. Essayez simplement si une «usine» ou un «localisateur de services» vous fournit ce dont vous avez besoin, ou si vous avez besoin d'autre chose. Si vous pensez que les deux solutions sont également bonnes pour votre cas, jetez un dé.


De bonnes questions! Veuillez voir la mise à jour.
candied_orange

Mis à jour avec le cas d'utilisation réel.
candied_orange

@CandiedOrange: Je ne peux pas vous donner de poisson, je peux seulement essayer de vous aider à pêcher par vous-même :-)
Doc Brown

Compris. Je regarde juste l'eau en me demandant si j'ai besoin d'un meilleur appât ou d'un autre trou de pêche. J'adorerais faire une DI correcte, mais cette situation ne semble pas le permettre.
candied_orange

1
@CandiedOrange Ne soyez pas accroché à un désir de faire de l'ID parce que c'est "bon" ou "mieux" qu'autre chose. DI est une solution particulière à un problème (ou parfois plusieurs). Mais ce n'est pas toujours la solution la plus «adaptée». N'en soyez pas amoureux ... traitez-le comme il est. C'est un outil.
svidgen

2

Juste pour être sûr de bien comprendre. Vous avez un service composé de certaines classes, disons, C1,...,Cnet d'un groupe de clients qui appellent directement new Ci(...). Je pense donc que je suis d'accord avec la structure générale de votre solution qui consiste à créer un nouveau service interne avec de nouvelles classes D1,...,Dnqui sont agréables et modernes et injecter leurs dépendances (permettant de telles dépendances à travers la pile), puis réécrire Cipour ne rien faire d'autre qu'instancier et delgate à Di. La question est de savoir comment faire et vous avez proposé une façon de couple 2et 3.

Pour donner une suggestion similaire à 2. Vous pouvez utiliser l'injection de dépendances tout au long Diet en interne, puis créer une racine de composition normale R(en utilisant un framework si vous le jugez approprié) qui est responsable de l'assemblage du graphe d'objet. Shove Rderrière une usine de statique et que chacun l' Ciobtenir est Dipar là. Ainsi, par exemple, vous pourriez avoir

public class OldClassI {
    private final NewClassI newImplementation;

    public OldClassI(Object parameter) {
        this.newImplementation = CompositionRoot.getInstance().getNewClassI(parameter);
    }
}

Il s'agit essentiellement de votre solution 2, mais elle rassemble toutes vos usines en un seul emplacement avec le reste de l'injection de dépendance.


Je suis d'accord avec ça. Vous ne savez pas comment ce n'est pas le même que le localisateur de service de l'option 3. Vous attendez-vous à créer une nouvelle instance à chaque appel à getInstance()?
candied_orange

@CandiedOrange C'est probablement juste un conflit d'utilisation, pour moi un localisateur de service est très spécifiquement quelque chose qui est essentiellement juste une table de hachage des types aux objets, alors que la racine de la composition n'est qu'un objet avec un tas de méthodes qui construisent d'autres objets.
walpen

1

Commencez par des implémentations simples, sans fantaisie et singulières.

Si vous devez par la suite créer des implémentations supplémentaires, il s'agit de savoir quand la décision d'implémentation se produit.

Si vous avez besoin d'une flexibilité "au moment de l'installation" (chaque installation client utilise une seule implémentation statique), vous n'avez toujours rien à faire de fantaisiste. Vous proposez simplement différentes DLL ou SO (ou quel que soit votre format de bibliothèque). Le client a juste besoin de mettre le bon dans le libdossier ...

Si vous avez besoin d'une flexibilité d'exécution, vous aurez juste besoin d'un adaptateur fin et d'un mécanisme de sélection d'implémentation. Que vous utilisiez une usine, un localisateur ou un conteneur IoC est principalement théorique. Les seules différences importantes entre un adaptateur et un localisateur sont A) la dénomination et B) si l'objet renvoyé est un singleton ou une instance dédiée. Et la grande différence entre un conteneur IoC et une usine / localisateur est qui appelle qui . (Souvent une question de préférence personnelle.)

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.