In der Branche besteht ein starker Konsens darüber, dass Service Locator ein Anti-Pattern ist. Aus dem Wiki:
Es ist erwähnenswert, dass der Service Locator in einigen Fällen tatsächlich ein Anti-Pattern ist.
In diesem Beitrag befasse ich mich mit einem Fall, in dem der Service Locator meiner Meinung nach kein Anti-Pattern ist.
Folgendes schreiben sie im Internet über Locator :
Einige betrachten Service Locator als Anti-Pattern. Es verstößt gegen das Prinzip der Abhängigkeitsinversion der SOLID- Prinzipien . Der Service Locator verbirgt die Abhängigkeiten einer bestimmten Klasse, anstatt sie freizugeben, wie dies beim Abhängigkeitsinjektionsmuster der Fall ist . Wenn sich diese Abhängigkeiten ändern, besteht die Gefahr, dass die Funktionalität der Klassen, die sie verwenden, beeinträchtigt wird, was die Wartung des Systems erschwert.
Service Locator geht so eng mit DI zusammen, dass einige Autoren (Mark Seemann, Steven van Deursen) ausdrücklich warnen :
Service Locator ist ein gefährliches Muster, da es fast funktioniert. ... Es gibt nur einen Bereich, in dem Service Locator zu kurz kommt, und das sollte nicht leicht genommen werden.
Das heißt, Locator ist verdammt gut und funktioniert fast so, wie es sollte, aber eines verdirbt alles. Da ist er:
Das Hauptproblem bei Service Locator ist die Auswirkung der Wiederverwendbarkeit der Klassen, die es verbrauchen. Dies äußert sich auf zwei Arten:
* Die Klasse zieht sich als redundante Abhängigkeit entlang des Service Locator .
* Die Klasse macht es nicht offensichtlich, was ihre Abhängigkeiten sind.
.., : -, - , -, , .
, :
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 dieser Version wird Service Locator zu einem sehr "Muster" ohne "Anti".