我在这里思考一些不同的方法,非常感谢一些输入!我正在考虑以下两个选择。有两件事情我会有问题。
是首选将依赖项注入主“container”类的构造函数中,还是在容器类中创建新实例?
在第二个示例中,类的依赖项通过构造函数注入,然后通过类的属性进行维护。然后,当调用方法(route(),render())时,从内部调用依赖项。我从这种方法开始,但现在我更倾向于第一个例子。我认为第一个例子是可取的,但在第二个例子中使用DI方法有什么好处吗?
确实没有必要将类中的任何内容存储为属性。我可以重新安排一切使用这种技术而不会有太多麻烦,我想我更喜欢它。这样我也可以将所有工作从构造函数中移出,然后通过方法简单地访问所有内容。我在这里走在正确的轨道上吗?
class App
{
private $config;
private $router;
private $renderer;
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
{
$this->config = $config;
$this->router = $router;
$this->renderer = $renderer;
$this->run();
}
public function run()
{
$data = $this->router->route(new Request, $config->routes);
$this->renderer->render($data);
}
}
class App
{
private $config;
private $router;
private $renderer;
public function __construct()
{
$this->config = new Config;
$this->run();
}
public function run()
{
$this->router = new Router(new Request, $config->routes);
$this->router->route();
$this->renderer = new Renderer($this->router->getData());
$this->renderer->render();
}
}
答案 0 :(得分:7)
最好将依赖项注入构造函数。
在构造函数中创建实例会在两个类之间创建紧密耦合。使用具有明确签名的构造函数,如
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
我可以立即告诉这个组件需要做什么工作。
给出像
这样的构造函数public function __construct();
无法确定组件需要运行的功能。它创建了与您的每个路由器,您的请求和渲染器的特定实现的强大耦合,在您深入了解您的类的内容之前,这些都不会显而易见。
总之,第一种方法有详细记录,可扩展和可测试。 第二种方法是不透明的,高度耦合的,不易测试。
答案 1 :(得分:2)
虽然Orangepill提出了一个很好的观点,但我认为我也会参与其中。我倾向于用一个清晰的构造函数来定义我的构造函数,但是我不希望期望在创建实例时传递所需的对象。
有时,您创建一个实例,从数据库或某种Http请求中检索 数据。在您的情况下,第一个示例需要传递三个依赖项,但是谁会说您将始终需要所有三个依赖项?
输入Lazy-Loading。下面的代码示例非常冗长,但它(IMO)非常值得研究。如果我使用服务,我不想加载所有依赖项,除非我确定我会使用它们。这就是我定义构造函数的原因,这样我就可以用以下任何一种方式创建一个实例:
$foo = new MyService($configObj);
$bar = new MyService($configObj, null, $dbObj);//don't load curl (yet)
$baz = new MyService($configObj, $curlObj);//don't load db (yet)
如果我想运行一些测试,我仍然可以在构造我的实例时注入依赖项,或者我可以依赖于test-config对象或我可以使用setDb
和setCurl
方法:
$foo->setCurl($testCurl);
坚持构造实例的第一种方法,我可以肯定地说,如果我只调用getViaCurl
方法,则永远不会加载Db
类。
getViaDb
方法稍微复杂一些(getDb
方法也是如此)。我不建议你使用这样的方法,但它只是向你展示这种方法可以的灵活性。我可以将一组参数传递给getViaDb
方法,该方法可以包含自定义连接。我还可以传递一个布尔值,它将控制我对该连接所做的操作(仅用于这一次调用,或者将连接分配给MyService
实例。
我希望这不太清楚,但我很累,所以我不太擅长解释ATM的这些东西。
无论如何,这是代码......它应该非常自我解释。
class MyService
{
private $curl = null;
private $db = null;
private $conf = null;
public function __construct(Config $configObj, Curl $curlObj = null, Db $dbObj = null)
{
$this->conf = $configObj;//you'll see why I do need this in a minute
$this->curl = $curlObj;//might be null
$this->db = $dbObj;
}
public function getViaCurl(Something $useful)
{
$curl = $this->getCurl();//<-- this is where the magic happens
return $curl->request($useful);
}
public function getViaDb(array $params)
{
if (isset($params['custom']))
{
$db = $this->getDb($params['custom'], $params['switch']);
}
else
{//default
$db = $this->getDb();
}
return $db->query($params['request']);
}
public function getCurl()
{//return current Curl, or load default if none set
if ($this->curl === null)
{//fallback to default from $this->conf
$this->curl = new Curl($this->conf->getSection('CurlConf'));
}
return $this->curl;
}
public function setCurl(Curl $curlObj)
{//inject after instance is created here
if ($this->curl instanceof Curl)
{//close current connection
$this->curl->close();
}
$this->curl = $curlObj;
}
public function setDb(Db $dbObj)
{
if ($this->db instanceof Db)
{//commit & close
$this->db->commit();
$this->db->close();
}
$this->db = $dbObj;
}
//more elaborate, even:
public function getDb(Db $custom = null, $switch = false)
{
if ($custom && !!$swith === true)
{
$this->setDb($custom);
return $this->db;
}
if ($custom)
{//use custom Db, only this one time
return $custom;
}
if ($this->db === null)
{
$this->db = new Db($this->conf->getSection('Db'));
}
return $this->db;
}
}