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.