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');
        }
    }
}

像這樣,每當我們使用核心功能時,即使沒有新增記錄器依賴項也不會中斷,即使可以新增另一個記錄器,也會使用新增的任何記錄器。我們正在擴充套件功能而不是替換它。