Il s'agit d'une décision de conception qui semble beaucoup revenir: comment passer du contexte à travers une méthode qui n'en a pas besoin à une méthode qui en a besoin. Y a-t-il une bonne réponse ou cela dépend-il du contexte?
Exemple de code nécessitant une solution
// needs the dependency
function baz(session) {
session('baz');
}
// doesn't care about the dependency
function bar() {
baz();
}
// needs the dependency
function foo(session) {
session('foo')
bar();
}
// creates the dependency
function start() {
let session = new Session();
foo(session);
}
Solutions possibles
- threadlocal
- global
- objet de contexte
- passer la dépendance
- curry baz et passez-le dans la barre avec la dépendance définie comme premier argument
- injection de dépendance
Exemples d'où vient
Traitement des requêtes HTTP
Les objets de contexte sous forme d'attributs de requête sont souvent utilisés: voir expressjs, Java Servlets ou owin de .net.
Enregistrement
Pour la journalisation Java, les gens utilisent souvent des globales / singletons. Voir les modèles typiques de journalisation log4j / commons logging / java.
Transactions
Les sections locales de thread sont souvent utilisées pour conserver une transaction ou une session associée à une chaîne d'appels de méthode pour éviter d'avoir à les transmettre en tant que paramètres à toutes les méthodes qui n'en ont pas besoin.
la source
Réponses:
La juste réponse est que cela dépend des idiomes de ParadigmMD de programmation. Si vous utilisez OO, il est presque certainement incorrect de passer une dépendance d'une méthode à l'autre. C'est une odeur de code dans OO. En fait, c'est l'un des problèmes que OO résout - un objet corrige un contexte. Donc, dans OO, une approche correcte (il y a toujours d'autres façons) est de fournir la dépendance via un constructeur ou une propriété. Un commentateur mentionne "Dependency Injection" et c'est parfaitement légitime, mais ce n'est pas strictement nécessaire. Fournissez simplement la dépendance afin qu'elle soit disponible en tant que membre de
foo
etbaz
.Vous mentionnez le curry, donc je suppose que la programmation fonctionnelle n'est pas hors de question. Dans ce cas, un équivalent philosophique du contexte d'objet est la fermeture. Toute approche qui, une fois de plus, fixe la dépendance il est donc disponible à charge fonctionne très bien. Le curry est une de ces approches (et il vous fait paraître intelligent). N'oubliez pas qu'il existe d'autres façons de fermer une dépendance. Certains d'entre eux sont élégants et certains horribles.
Ne pas oublier la programmation par aspects . Il semble être tombé en disgrâce ces dernières années, mais son objectif principal est de résoudre exactement le problème que vous décrivez. En fait, l'exemple d'aspect classique se connecte. Dans AOP, la dépendance est ajoutée automatiquement après l'écriture d'un autre code. Les gens AOP appellent cela « tissage ». Les aspects communs sont tissés dans le code aux endroits appropriés. Cela rend le code plus facile de réfléchir et est assez cool de reprise, mais il ajoute également une nouvelle charge d'essai. Vous aurez besoin d'un moyen de déterminer si vos artefacts finaux sont sains. AOP a aussi des réponses à cela, alors ne vous sentez pas intimidé.
la source
Si
bar
dépendbaz
, ce qui nécessite à son tourdependency
, alorsbar
nécessitedependency
aussi afin d'utiliser correctementbaz
. Par conséquent, les approches correctes consisteraient soit à transmettre la dépendance en tant que paramètre àbar
, soit à currybaz
et à la transmettre àbar
.La première méthode est plus simple à mettre en œuvre et de lire, mais crée un couplage entre
bar
etbaz
. La deuxième approche supprime ce couplage, mais pourrait entraîner un code moins clair. Ce qui est la meilleure approche dépend donc probable de la complexité et le comportement des deux fonctions. Par exemple, sibaz
oudependency
avoir des effets secondaires, facilité d'essais sera sans doute un grand pilote dans lequel la solution choisie.Je suggérerais que toutes les autres options que vous proposez sont à la fois "hacky" par nature et susceptibles d'entraîner des problèmes à la fois avec les tests et avec des bogues difficiles à détecter.
la source
dependency
via les paramètres est l'injection de dépendance?Parler philosophiquement
Je suis d' accord avec la préoccupation de David Arno .
Je lis l'OP comme la recherche de solutions de mise en œuvre. Cependant, la réponse est changer la conception . "Motifs"? Conception OO est, pourrait - on dire, question de contexte. Il est une grande, une feuille de papier vierge enceinte de possibilités.
Le traitement du code existant est un contexte bien différent.
Je travaille sur « zactly le même problème en ce moment. Eh bien, je corrige les centaines de lignes de copier-coller de code qui ont été effectuées juste pour qu'une valeur puisse être injectée.
Modularisation du code
J'ai jeté 600 lignes de code en double puis refactorisé donc au lieu de "A appelle B appelle C appelle D ..." J'ai "Appel A, retour, Appel B, retour, Appel C ...". Il ne nous reste plus qu'à injecter la valeur dans l'une de ces méthodes, disons la méthode E.
Ajout d'un paramètre par défaut au constructeur. Les appelants existants ne changent pas - "facultatif" est le mot clé ici. Si aucun argument n'est transmis, la valeur par défaut est utilisée. Ensuite, une seule ligne change pour passer la variable dans la structure modulaire refactorisée; et un petit changement dans la méthode E pour l'utiliser.
Fermetures
Un thread de programmeurs - "Pourquoi un programme utiliserait-il une fermeture?"
Pour l'essentiel, vous injectez des valeurs dans une méthode qui retourne une méthode sur mesure avec les valeurs. Cette méthode mesure est ensuite exécuté.
Cette technique vous permettrait de modifier une méthode existante sans changer sa signature.
la source