他們是不同的,並沒有真正提倡一個在另一個。
(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