可能重复:
Dependency Hell — how does one pass dependencies to deeply nested objects?
在一个围绕强依赖注入构建的系统中,我想知道如何处理这样一个人为的情况:
<?php
class LogWriter
{
public function write(Log $log)
{
echo $log->getMessage();
}
}
class Log
{
private $message;
public function setMessage($message)
{
$this->message = $message;
}
public function getMessage()
{
return $this->message;
}
}
class Logger
{
private $writer;
public function __construct(LogWriter $writer)
{
$this->writer = $writer;
}
public function write($message)
{
// Here is the dependency
$log = new Log();
$log->setMessage($message);
$this->writer->write($log);
}
}
Logger :: write()方法创建一个Log实例,并将其传递给日志编写器。我的直觉告诉我这是一个糟糕的方法,从现在起一个月我将跟踪一个与之相关的错误,我可能希望在测试期间将Log类切换为其他内容。
但如何避免呢?我唯一想到的是将Log 类型传递给Logger构造函数,并将我的Logger类更改为:
class Logger
{
private $writer;
private $log_type;
public function __construct(LogWriter $writer, $log_type)
{
$this->writer = $writer;
$this->log_type = $log_type;
}
public function write($message)
{
$log = new $this->log_type();
$log->setMessage($message);
$this->writer->write($log);
}
}
然后创建一个新的Logger实例,如下所示:
$log_writer = new LogWriter();
$logger = new Logger($log_writer, "Log");
但这感觉有点hackish。那么你如何处理像这样的微依赖?
注意:我使用日志记录类作为示例,我不是在寻找解决这个问题的方法。我可能只使用数组而不是Log类。
编辑:在更复杂的情况下,我可能会将依赖注入容器传递给Logger类,并使用它来创建Log实例,但对于简单的记录器类来说,这似乎过于复杂。
答案 0 :(得分:3)
由于您的Log对象实际上只是数据传输对象或值对象,因此您可以在Logger类中创建它。在这种情况下这样做是可以的。您不需要将任何内容传递给Logger。但你正确的是you wont be able to mock/stub this easily then.
作为替代方案,如果要将Log类与Logger分离,也可以注入Factory:
$logger = new Logger($logWriter, new LogFactory);
然后从那里创建日志类型:
public function write($message)
{
$log = $this->logFactory->createNew();
…
这将创建逻辑封装在Factory类中。工厂内部仍然会有Log
类型的硬编码,但工厂可以拥有它。然后,只需在调用createNew
时测试它是否返回正确的类型。在您的消费者中,您可以存根该呼叫。
如果你不想为此创建一个完整的工厂类,你也可以使用Lambda而不是Factory。因为它捕获了必要的创建逻辑,所以它实际上与工厂相同,只是没有类:
$logger = new Logger($logWriter, function() { return new Log; });
然后
public function write($message)
{
$log = call_user_func($this->createLogCallback);
…
Factoy和Lambda方法都允许在单元测试中替换Log类型。然后,在您的方案中,替换Log类型似乎不是必需的。 Log
类型没有任何自己的依赖关系,所以你可以在这里使用真正的交易。您只需查看write
撰写的内容,即可轻松验证LogWriter
方法。你不会在Log Mock上有明确的断言,但是如果writer为给定的输入生成write
的预期输出,你可以放心地假设Log
类型按预期协作。
另请参阅http://misko.hevery.com/2008/09/30/to-new-or-not-to-new了解详情。