setter 注入
依赖关系也可以由 setter 注入。
interface Logger {
public function log($message);
}
class Component {
private $logger;
private $databaseConnection;
public function __construct(DatabaseConnection $databaseConnection) {
$this->databaseConnection = $databaseConnection;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function core() {
$this->logSave();
return $this->databaseConnection->save($this);
}
public function logSave() {
if ($this->logger) {
$this->logger->log('saving');
}
}
}
当类的核心功能不依赖于依赖工作时,这尤其有趣。
在这里,唯一需要的依赖是 DatabaseConnection
所以它在构造函数中。Logger
依赖项是可选的,因此不需要是构造函数的一部分,使类更容易使用。
请注意,使用 setter 注入时,最好扩展功能而不是替换它。设置依赖项时,没有任何事情可以确认依赖项在某些时候不会发生变化,这可能会导致意外结果。例如,首先可以设置 FileLogger
,然后可以设置 MailLogger
。这打破了封装并使得日志很难找到,因为我们正在替换依赖。
为了防止这种情况,我们应该使用 setter 注入添加依赖项,如下所示:
interface Logger {
public function log($message);
}
class Component {
private $loggers = array();
private $databaseConnection;
public function __construct(DatabaseConnection $databaseConnection) {
$this->databaseConnection = $databaseConnection;
}
public function addLogger(Logger $logger) {
$this->loggers[] = $logger;
}
public function core() {
$this->logSave();
return $this->databaseConnection->save($this);
}
public function logSave() {
foreach ($this->loggers as $logger) {
$logger->log('saving');
}
}
}
像这样,每当我们使用核心功能时,即使没有添加记录器依赖项也不会中断,即使可以添加另一个记录器,也会使用添加的任何记录器。我们正在扩展功能而不是替换它。