Я напишу длинный вопрос с короткой версией вопроса:
Короткий вариант вопроса
Что не так, что позволяет объекту создавать свои собственные зависимости, а затем предоставлять аргументы конструктора (или методы setter) для просто переопределения экземпляров по умолчанию?
class House
{
protected $door;
protected $window;
protected $roof;
public function __construct(IDoor $door = null, IWindow $window = null, IRoof $roof = null)
{
$this->door = ($door) ? $door : new Door;
$this->window = ($window) ? $window : new Window;
$this->roof = ($roof) ? $roof : new Roof;
}
}
Длинная версия вопроса
Моя мотивация для этого вопроса заключается в том, что инъекция зависимостей требует, чтобы вы прыгали через обручи, чтобы дать объекту то, что ему нужно. Контейнеры IoC, заводы, локаторы сервисов... все это приводит к множеству дополнительных классов и абстракций, которые усложняют API вашего приложения, и я бы сказал, что во многих случаях тестирование так же сложно.
Не логично ли, что объект действительно знает, какие зависимости ему нужны, чтобы нормально функционировать?
Если две основные мотивации инъекций зависимостей - это повторная юзабилити кода и возможность тестирования единиц, то возможность переопределения экземпляров по умолчанию с помощью заглушек или других объектов делает это очень хорошо.
Между тем, если вам нужно добавить класс House к вашему приложению, ТОЛЬКО нужно закодировать класс House, а не factory и/или контейнер DI поверх него. Кроме того, любой клиентский код, который использует дом, может просто включать дом, и ему не нужно давать дом factory или абстрактный сервисный локатор где-то сверху. Все становится чрезвычайно прямым, без кода посредника, и создается только тогда, когда это необходимо.
Неужели я совершенно не согласен с тем, что если у объекта есть зависимости, он должен иметь возможность загружать их самостоятельно, предоставляя механизм для перегрузки этих зависимостей при желании?
Пример
#index.php (front controller)
$db = new PDO(...);
$cache = new Cache($dbGateway);
$session = new Session($dbGateway);
$router = new Router;
$router::route('/some/route', function() use ($db, $cache, $session)
{
$controller = new SomeController($db, $cache, $session);
$controller->doSomeAction();
});
#SomeController.php
class SomeController
{
protected $db;
protected $cache;
protected $session;
public function __construct(PDO $db, ICache $cache, ISession $session)
{
$this->db = $db;
$this->cache = $cache;
$this->session = $session;
}
public function doSomeAction()
{
$user = new \Domain\User;
$userData = new \Data\User($this->db);
$user->setName('Derp');
$userData->save($user);
}
}
Теперь, в очень большом приложении со многими различными классами/классами данных и контроллерами, я чувствую, что мне нужно передать объект БД ЧЕРЕЗ каждый контроллер (который ему не понадобится), чтобы передать его каждому преобразователю данных ( который ему понадобится), немного вонючий.
И по расширению, передавая локатор сервисов или контейнер DI через контроллер, просто для того, чтобы найти базу данных, а затем передать ее в datamapper каждый раз, также кажется немного вонючим.
То же самое касается передачи factory или абстрактного factory через контроллер, а затем необходимость создания новых объектов с помощью чего-то громоздкого типа $this->factory->make('\Data\User');
кажется неудобным. Тем более, что вам нужно закодировать абстрактный factory класс, а затем фактический factory, который связывает зависимости для требуемого объекта.