我将用问题的简短版本作为长问题的序言:
问题的简短版本
允许对象实例化它自己的依赖项,然后提供构造函数参数(或设置方法)来简单地覆盖默认实例化有什么问题?
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 类,而不是在其之上的工厂和/或 DI 容器。此外,任何使用房屋的客户端代码都可以只包含房屋,并且不需要从上面的某个地方获得房屋工厂或抽象服务定位器。一切都变得非常简单,没有中间人代码,并且仅在需要时才实例化。
如果一个对象有依赖关系,它应该能够自己加载它们,同时提供一种机制让这些依赖关系在需要时被重载,我是否完全不合时宜?
例子
#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);
}
}
现在,在一个具有许多不同模型/数据类和控制器的非常大的应用程序中,我觉得必须通过每个控制器(不需要它)传递 DB 对象只是为了将它提供给每个数据映射器(这将需要它),有点臭。
而且通过控制器传递一个服务定位器或DI容器,只是为了定位数据库然后每次将它交给数据映射器,也似乎有点臭。
将工厂或抽象工厂传递给控制器也是如此,然后不得不通过一些$this->factory->make('\Data\User');
看起来很笨拙的东西来实例化新对象。特别是因为您需要编写抽象工厂类,然后是为您想要的对象连接依赖关系的实际工厂。