¿Es este localizador de servicios un anti-patrón?

Existe un fuerte consenso en la industria de que Service Locator es un anti-patrón. De la wiki:





Vale la pena señalar que, en algunos casos, el localizador de servicios es en realidad un anti-patrón.





En esta publicación, estoy viendo un caso en el que, en mi opinión, el Localizador de servicios no es un anti-patrón.





Esto es lo que escriben en Internet sobre Locator :





Algunos consideran que el localizador de servicios es un anti-patrón. Viola el principio de inversión de dependencia del conjunto de principios  SOLID . El localizador de servicios oculta las dependencias de una clase determinada en lugar de compartirlas, como es el caso del patrón de inyección de dependencia . Si estas dependencias cambian, corremos el riesgo de romper la funcionalidad de las clases que las utilizan, lo que dificulta el mantenimiento del sistema.





Service Locator va tan de la mano con DI que algunos autores (Mark Seemann, Steven van Deursen) advierten específicamente :





El localizador de servicios es un patrón peligroso porque casi funciona. ... Solo hay un área en la que Service Locator se queda corto, y eso no debe tomarse a la ligera.





Es decir, Locator es bastante bueno y funciona casi como debería, pero hay una cosa que lo estropea todo. Aquí lo tienes:





El principal problema con  Service Locator es el impacto de la reutilización de las clases que lo consumen. Esto se manifiesta de dos formas:









* La clase arrastra el  localizador de servicios  como una dependencia redundante  .





* La clase hace que no sea obvio cuáles  son sus  dependencias .





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





, :





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);
      
      



En esta versión, Service Locator se convierte en un "patrón" sin ningún "anti".








All Articles