设置程序也可以注入依赖项。
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可以首先设置a ,然后再MailLogger设置a。这会破坏封装并使日志难以查找,因为我们正在替换依赖项。
为了防止这种情况,我们应该在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'); } } }
这样,每当我们使用核心功能时,即使没有添加记录器依赖项,它也不会中断,并且即使可以添加另一个记录器,添加的记录器也将被使用。我们正在扩展功能,而不是取代它。