他们是不同的,并没有真正提倡一个在另一个。
(1)被广泛称为构造函数依赖注入。它被认为是更好的,首选的形式,而不是(2)。这些依赖关系对消费者是“隐藏的”,通常在对象生命周期中不会改变。
考虑抽象的HTTP客户端:
$httpClient = new HttpClient(new CurlAdapter());
// or
$httpClient = new HttpClient(new SocketAdapter());
开关电源适配器,不影响客户是如何使用的:因为它强制执行依赖注入
$response = $httpClient->get($url);
构造DI提倡优越于setter方法。此外,setter通常允许在对象生命周期中改变依赖关系,改变它的行为并打开可能的棘手难以调试的错误。 (2)在不同的使用情况下使用,通常不与DI重叠。(2)在不同的使用情况下使用,通常不与DI重叠。 以这种方式传递依赖关系有各种原因。它们可能是交互界面的一部分,在对象生命周期中经常变化或在通话时间决定。依赖关系在概念上可能不属于对象,但仍然需要该特定操作。重要的是他们不应该存储在对象中,并以可能导致副作用的方式使用!
class SomeClass
{
protected $dep;
public function doSomething(DepInterface $dep)
{
// do the stuff
// store dep for later
$this->dep = $dep;
// This is similar to issue with setters but much worse.
// Side effect is not obvious
}
public function doSomethingElse()
{
$this->dep->increment();
}
}
for ($i = 0; $i < 100; $i++) {
$foo->doSomething($bar);
if (rand(0, 100) == $i) {
// represents conditions out of direct control.
// such as conditional or timing errors, alternative flows,
// unanticipated code changes
$foo->doSomethng($baz);
}
$foo->doSomethingElse();
}
// what will be the result?
考虑这个DDD比如解决一些不切实际的问题:
class Bus
{
public function board(Passenger $passenger, TicketingService $tservice)
{
// @todo check bus have seats
// throws exception if ticket is invalid
$tservice->punchPassengerTicket($this, $passenger);
$this->passengers[] = $passenger;
}
}
if ($bus->isIntercity()) {
$ticketingService = new BookingService();
} else {
$ticketingService = new PrepaidCityTransportCardsService();
}
$bus->board($passenger, $ticketingService);
在这个例子中,我们明确地强制寄宿总线需要门票和车票都在登机打孔。但是,如果我们要在Bus实例化中注入TicketingService,即使对于根本没有登机的情况,我们也会得到更复杂的依赖关系图。通过服务执行操作,但从不存储它会降低复杂性,并在此情况下显着提高可测试性。
该技术在域驱动设计环境中被称为双派遣(不要与Double Dispatch混淆)并广泛使用。
您可能认为我们可以在登机前检查车票,并完全取消对票务服务的依赖。那么,这是另一个话题。
在第一个,一旦你构造,就不再需要之前正确地设置不断将这些对象传递给方法。在第二种情况下,你必须不断地传入对象。如果每次传入'doStuff'时对象不同,但是如果我们正在讨论依赖注入*,那么首选项是首选。 –
区别在于你必须在调用第二个方法之前在你的主代码中的某处实例化对象。那不一定是坏的,只是不同而已 – RiggsFolly