Récemment, j'ai lu l'article de Mark Seemann sur l'anti-pattern Service Locator.
L'auteur souligne deux raisons principales pour lesquelles ServiceLocator est un anti-pattern:
Problème d'utilisation de l'API (avec lequel je suis parfaitement d'accord)
Lorsque la classe utilise un localisateur de service, il est très difficile de voir ses dépendances car, dans la plupart des cas, la classe n'a qu'un seul constructeur PARAMETERLESS. Contrairement à ServiceLocator, l'approche DI expose explicitement les dépendances via les paramètres du constructeur afin que les dépendances soient faciles à voir dans IntelliSense.
Problème de maintenance (qui me laisse perplexe) Considérez l'exemple suivant
Nous avons une classe 'MyType' qui utilise une approche de localisateur de service:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Maintenant, nous voulons ajouter une autre dépendance à la classe 'MyType'
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
Et c'est ici que commence mon malentendu. L'auteur dit:
Il devient beaucoup plus difficile de dire si vous introduisez un changement radical ou non. Vous devez comprendre toute l'application dans laquelle le localisateur de services est utilisé et le compilateur ne vous aidera pas.
Mais attendez une seconde, si nous utilisions l'approche DI, nous introduirions une dépendance avec un autre paramètre dans le constructeur (en cas d'injection de constructeur). Et le problème sera toujours là. Si nous oublions de configurer ServiceLocator, nous pouvons oublier d'ajouter un nouveau mappage dans notre conteneur IoC et l'approche DI aurait le même problème d'exécution.
En outre, l'auteur a mentionné les difficultés des tests unitaires. Mais n'aurons-nous pas de problèmes avec l'approche DI? N'aurons-nous pas besoin de mettre à jour tous les tests qui instanciaient cette classe? Nous les mettrons à jour pour passer une nouvelle dépendance simulée juste pour rendre notre test compilable. Et je ne vois aucun avantage à cette mise à jour et au temps passé.
Je n'essaye pas de défendre l'approche de Service Locator. Mais ce malentendu me fait penser que je perds quelque chose de très important. Quelqu'un pourrait-il dissiper mes doutes?
MISE À JOUR (RÉSUMÉ):
La réponse à ma question "Service Locator est-il un anti-pattern" dépend vraiment des circonstances. Et je ne suggérerais certainement pas de le rayer de votre liste d'outils. Cela peut devenir très pratique lorsque vous commencez à utiliser du code hérité. Si vous avez la chance d'être au tout début de votre projet, l'approche DI peut être un meilleur choix car elle présente certains avantages par rapport à Service Locator.
Et voici les principales différences qui m'ont convaincu de ne pas utiliser Service Locator pour mes nouveaux projets:
- Le plus évident et le plus important: Service Locator masque les dépendances de classe
- Si vous utilisez un conteneur IoC, il analysera probablement tous les constructeurs au démarrage pour valider toutes les dépendances et vous donner un retour immédiat sur les mappages manquants (ou une mauvaise configuration); ce n'est pas possible si vous utilisez votre conteneur IoC comme localisateur de service
Pour plus de détails, lisez les excellentes réponses données ci-dessous.
Réponses:
Si vous définissez des motifs comme des anti-motifs simplement parce qu'il y a des situations où cela ne correspond pas, alors OUI c'est un anti-motif. Mais avec ce raisonnement, tous les modèles seraient également des anti-modèles.
Au lieu de cela, nous devons vérifier s'il existe des utilisations valides des modèles, et pour Service Locator, il existe plusieurs cas d'utilisation. Mais commençons par regarder les exemples que vous avez donnés.
Le cauchemar de maintenance avec cette classe est que les dépendances sont cachées. Si vous créez et utilisez cette classe:
Vous ne comprenez pas qu'il a des dépendances si elles sont masquées à l'aide de l'emplacement du service. Maintenant, si nous utilisons plutôt l'injection de dépendances:
Vous pouvez directement repérer les dépendances et ne pouvez pas utiliser les classes avant de les satisfaire.
Dans une application métier type, vous devez éviter d'utiliser l'emplacement de service pour cette raison même. Ce devrait être le modèle à utiliser lorsqu'il n'y a pas d'autres options.
Le motif est-il un anti-motif?
Non.
Par exemple, l'inversion des conteneurs de contrôle ne fonctionnerait pas sans l'emplacement du service. C'est ainsi qu'ils résolvent les services en interne.
Mais un bien meilleur exemple est ASP.NET MVC et WebApi. Selon vous, qu'est-ce qui rend possible l'injection de dépendances dans les contrôleurs? C'est vrai - l'emplacement du service.
Vos questions
Il y a deux problèmes plus graves:
Avec l'injection de constructeur à l'aide d'un conteneur, vous obtenez cela gratuitement.
C'est vrai. Mais avec l'injection de constructeur, vous n'avez pas besoin d'analyser toute la classe pour déterminer quelles dépendances sont manquantes.
Et certains meilleurs conteneurs valident également toutes les dépendances au démarrage (en analysant tous les constructeurs). Donc, avec ces conteneurs, vous obtenez l'erreur d'exécution directement, et non à un moment temporel ultérieur.
Non. Comme vous n'avez pas de dépendance à un localisateur de service statique. Avez-vous essayé de faire fonctionner des tests parallèles avec des dépendances statiques? Ce n'est pas amusant.
la source
Je tiens également à souligner que SI vous refactorisez le code hérité, le modèle Service Locator n'est pas seulement non pas un anti-modèle, mais c'est aussi une nécessité pratique. Personne ne va jamais agiter une baguette magique sur des millions de lignes de code et tout à coup tout ce code sera prêt pour la DI. Donc, si vous voulez commencer à introduire DI dans une base de code existante, il arrive souvent que vous changiez les choses pour devenir des services DI lentement, et le code qui référence ces services ne sera souvent PAS des services DI. Par conséquent, CES services devront utiliser le localisateur de services afin d'obtenir des instances de ces services qui ONT été convertis pour utiliser DI.
Ainsi, lors de la refactorisation de grandes applications héritées pour commencer à utiliser des concepts DI, je dirais que non seulement Service Locator n'est PAS un anti-modèle, mais que c'est le seul moyen d'appliquer progressivement les concepts DI à la base de code.
la source
Du point de vue des tests, Service Locator est mauvais. Voir la belle explication Google Tech Talk de Misko Hevery avec des exemples de code http://youtu.be/RlfLCWKxHJ0 à partir de 8h45. J'ai aimé son analogie: si vous avez besoin de 25 $, demandez directement de l'argent plutôt que de donner votre portefeuille d'où l'argent sera pris. Il compare également Service Locator à une botte de foin qui contient l'aiguille dont vous avez besoin et sait comment la récupérer. Les classes utilisant Service Locator sont difficiles à réutiliser à cause de cela.
la source
Il y a 2 raisons différentes pour lesquelles l'utilisation du localisateur de services est mauvaise à cet égard.
Clair et simple: une classe avec un localisateur de services est plus difficile à réutiliser qu'une classe qui accepte ses dépendances via son constructeur.
la source
Mes connaissances ne sont pas assez bonnes pour en juger, mais en général, je pense que si quelque chose a une utilité dans une situation particulière, cela ne signifie pas nécessairement qu'il ne peut pas être un anti-modèle. Surtout, lorsque vous traitez avec des bibliothèques tierces, vous n'avez pas un contrôle total sur tous les aspects et vous risquez de finir par utiliser la meilleure solution.
Voici un paragraphe de Adaptive Code via C # :
la source
L'auteur raisonne que "le compilateur ne vous aidera pas" - et c'est vrai. Lorsque vous daignerez une classe, vous voudrez bien choisir son interface - entre autres objectifs pour la rendre aussi indépendante que ... car cela a du sens.
En demandant au client d'accepter la référence à un service (à une dépendance) via une interface explicite, vous
Vous avez raison de dire que DI a ses problèmes / inconvénients, mais les avantages mentionnés les dépassent de loin ... IMO. Vous avez raison, avec DI, il y a une dépendance introduite dans l'interface (constructeur) - mais c'est, espérons-le, la dépendance même dont vous avez besoin et que vous voulez rendre visible et vérifiable.
la source
Oui, le localisateur de service est un anti-modèle, il viole l'encapsulation et le solide .
la source