Ce localisateur de service est-il un tel anti-modèle?

Il existe un fort consensus dans l'industrie selon lequel Service Locator est un anti-pattern. Depuis le wiki:





Il est à noter que dans certains cas, le localisateur de services est en fait un anti-pattern.





Dans cet article, je regarde un cas où, à mon avis, le localisateur de service n'est pas un anti-modèle.





Voici ce qu'ils écrivent sur Internet à propos de Locator :





Certains considèrent Service Locator comme un anti-pattern. Il viole le principe d'inversion de dépendance de l'ensemble de principes  SOLID . Le localisateur de service masque les dépendances d'une classe donnée au lieu de les partager, comme c'est le cas avec le modèle d' injection de dépendances . Si ces dépendances changent, nous risquons de casser la fonctionnalité des classes qui les utilisent, ce qui rend difficile la maintenance du système.





Service Locator va de pair avec DI si étroitement que certains auteurs (Mark Seemann, Steven van Deursen) avertissent spécifiquement :





Service Locator est un modèle dangereux car il fonctionne presque. ... Il n'y a qu'un seul domaine dans lequel le localisateur de service ne fonctionne pas, et cela ne doit pas être pris à la légère.





Autrement dit, Locator est vraiment bon et fonctionne presque comme il se doit, mais il y a une chose qui gâche tout. C'est ici:





Le principal problème avec  Service Locator est l'impact de la réutilisabilité des classes qui le consomment. Cela se manifeste de deux manières:









* La classe traîne le long du  localisateur de service en  tant que dépendance redondante  .





* La classe rend non évidentes ses  dépendances  .





.., : -, - , -, , .





, :





public function __construct(IDep1 $dep1, IDep2 $dep2, IDep3 $dep3)
{
    $this->dep1 = $dep1;
    $this->dep2 = $dep2;
    $this->dep3 = $dep3;
}
      
      



- :





public function __construct(ILocator $locator)
{
    $this->locator = $locator;
    $this->dep1 = $locator->get(IDep1::class);
    $this->dep2 = $locator->get(IDep2::class);
    $this->dep3 = $locator->get(IDep3::class);
}
      
      



() (, ):





Property Injection should only be used when the class you’re developing has a good Local Default, and you still want to enable callers to provide different implementations of the class’s Dependency. It’s important to note that Property Injection is best used when the Dependency is optional. If the Dependency is required, Constructor Injection is always a better pick.





:





public function __construct(ILocator $locator = null)
{
    if ($locator) {
        $this->dep1 = $locator->get(IDep1::class);
    }
}

public function setDep1(IDep1 $dep1)
{
    $this->dep1 = $dep1;
}
      
      



, ) (, ), ) setter' ( , , "" , Ctrl+F "$locator->get" ).





, , , . " Dependency Injection Service Locator?" @symbix :





SL pull: "" .





DI push: .





.., , DI- Service Locator:





// push deps into constructor
public function __construct(IDep1 $dep1, IDep2 $dep2, IDep3 $dep3) {}

// pull deps from constructor
public function __construct(IContainer $container) {
    if ($container) {
        $this->dep1 = $container->get(IDep1::class);
        $this->dep2 = $container->get(IDep2::class);
        $this->dep3 = $container->get(IDep3::class);
    }
}
      
      



, , - -. ? , , , , - .. . .., , , , .





"-" Service Locator "" :





class App {
    /** @var \IContainer */
    private $container;
    /** @var \IDep1 */
    private $dep1;

    public function __construct(IContainer $container = null) {
        $this->container = $container;
    }

    private function initDep1() {
        if (!$this->dep1) {
            $this->dep1 = $this->container->get(IDep1::class);
        }
        return $this->dep1;
    }

    public function run() {
        $dep1 = $this->initDep1();
    }

    public function setDep1(IDep1 $dep1) {
        $this->dep1 = $dep1;
    }

}
      
      



, :





  • setter (, );





  • private- init



    ;





  • , .





Service Locator . - ( "push") DI- , . "pull" , :





$this->dep1 = $this->container->get(IDep1::class, self::class);
      
      



Dans cette version, Service Locator devient un très "pattern" sans aucun "anti".








All Articles