Is this Service Locator such an anti-pattern?

There is a strong consensus in the industry that Service Locator is an anti-pattern. From the wiki:





It is worth noting that in some cases the service locator is actually an anti-pattern.





In this post, I'm looking at a case where, in my opinion, the Service Locator is not an anti-pattern.





Here's what they write on the Internet about Locator :





Some consider Service Locator to be an anti-pattern. It violates the Dependency Inversion principle of the SOLID set of principles  . The Service Locator hides the dependencies of a given class instead of sharing them, as is the case with the Dependency Injection pattern . If these dependencies change, we risk breaking the functionality of the classes that use them, which makes it difficult to maintain the system.





Service Locator goes hand in hand with DI so closely that some authors (Mark Seemann, Steven van Deursen) specifically warn :





Service Locator is a dangerous pattern because it almost works. ... There's only one area where Service Locator falls short, and that shouldn't be taken lightly.





That is, Locator is pretty damn good and works almost as it should, but there is one thing that spoils everything. Here it is:





The main problem with  Service Locator 's the impact of reusability of the classes consuming it. This manifests itself in two ways:









* The class drags along the  Service Locator  as a redundant  Dependency .





* The class makes it non-obvious what its  Dependencies  are.





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





, :





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



In this version the Service Locator becomes a very "pattern" without any "anti".








All Articles