2014-02-13 23 views
3

我在設計類時遇到了兩難的問題。 我盡我所能去尊重SOLID原則,但我不知道如何處理依賴注入。如何在一個完整的面向對象應用程序中處理依賴注入

這裏是我的困境:

  • 我讀這是一個不好的做法來實例化內部類的對象,以避免產生依賴。那麼我們的依賴應該在完整的對象應用程序中創建?在只負責依賴實例的特殊對象中?如果是的話,這個對象的名字是什麼,以及如何定義它?是我們所說的「控制器」嗎?
  • 這個「控制器」,單元測試的正確方法是什麼?我們應該單元測試它嗎?
  • 在完整的POO應用程序中,如何避免在類之間傳遞對象(通常是相同的)?例如,一個DB對象Log,...通過這種方式,我們冒險讓構造函數有很多參數,不是嗎?

爲了說明我的困境,我試圖創建一個用例。

我想創建生成文件的腳本(我在下面部分實現)和打印另一個問題:

<?php 

/** 
* Generate a file, add it to the queue and print the next one 
*/ 
class Script 
    public function run() { 
     #Generate file 
     $fileComputor = new FileComputer(...); 
     $file = $fileComputor->compute(); 

     #Instantiate dependencies for printing 
     $db = new Db(new PdoAdapter()); 
     $printerDriver = new Driver(new HttpRequest(new CurlAdapter())); 
     $log = new Log($db); 
     $queue = new Queue($db); 
     $statsUpdater = new StatsUpdater($db); 
     $monitor = new Monitor(new StatsdDriver(new SocketAdapter())); 

     #Add generated file to the queue 
     $queueModel->push($file); 

     #Print the next file on queue if existing 
     $printer = new Printer($printerDriver, $log, $queue, $monitor, $statsUpdater); 
     $printer->print(); 
    } 
} 

class Printer { 
    protected $_driver; 
    protected $_log; 
    protected $_queue; 
    protected $_monitor; 
    protected $_statsUpdater; 

    /** 
    * $driver   : Driver used to send documents to the printer 
    * $log    : Log actions in database 
    * $queue   : Handle the print queue 
    * $monitor   : Send metrics to Statsd (to feed the graphit GUI) 
    * $statsdUpdater : Consolidate the statistics database 
    */ 
    public function __construct($driver, $log, $queue, $monitor, $statsUpdater) { 
     $this->_driver = $driver; 
     $this->_log = $log; 
     $this->_queue = $queue; 
     $this->_monitor = $monitor 
     $this->_statsUpdater = $statsUpdater; 
    } 

    public function print() { 
     if ($this->_queue->hasNext()) { 
      $file = $this->_queue->getNext(); 

      $this->_driver->print($file); 

      $this->_log->log('File has been printed'); 
      $this->_monitor->sendCount(1); 

      $this->_statsUpdater->increment(); 
     } 
    } 
} 

?> 

你覺得這個怎麼樣實現?

我們希望插入到我們的Printer類中的每個功能都會導致傳遞給構造函數的新依賴項(例如,如果我們還想生成syslog,測量打印機處理的時間等) 。

在不久的將來,我們將有10到15個參數進入構造函數調用。

+0

你'腳本'類有什麼意義?這不是Java,你不需要在「僅僅因爲」類中包裝腳本。 –

+0

這是PHP。事實上,我調用了Script類,因爲它實際上擴展了一個ScriptAbstract類(能夠處理參數並打印一個用例示例),但我沒有將它精確化,以免混淆。在真實情況下,類使用傳遞給腳本的參數並將它們發送到FileComputer和Printer類。 –

回答

6

那麼我們的依賴關係應該在完整的對象應用程序中創建?在只負責依賴實例的特殊對象中?

你有2種選擇:

  • 您創建例如前端控制器(index.php文件)中的所有對象自己,你的應用程序,即根。如果你的應用程序有點大,那很快就會變成地獄。您使用依賴注入容器。該對象將負責創建對象(並在構造函數中注入它們的依賴關係)。同樣在這裏:你只能在應用程序的根目錄下使用/調用容器,例如在前端控制器(index.php)中。

如果是,該對象的名稱以及如何定義它?是我們所說的「控制器」嗎?

這就是容器。舉個例子,這裏是PHP-DI - Understanding DI。你可以在控制器上使用依賴注入(我建議這麼做):你可以在控制器的構造函數中獲得依賴(就像在任何服務中一樣)。一些框架使得這很困難(例如Symfony)。

這個「控制器」,單元測試的正確方法是什麼?我們應該單元測試它嗎?

沒有。有些容器允許你配置「工廠」來生成一些對象。

例如,如果創建DBConnection對象很複雜,則可以編寫一個工廠類,該工廠類具有創建DBConnection對象的方法。所以你可以測試工廠類。但我不會說這是必要的。

在一個完整的POO應用程序中,如何避免在類之間傳遞我們的對象(通常是相同的)?

您不應該傳遞實例,因爲您永遠不應該調用構造函數:所有對象都由容器構造。

因此,它變得非常簡單:您通過在構造函數中使用依賴關係編寫每個類,就是這樣。你不關心依賴關係和那些依賴關係需要的東西。

例如,一個DB對象,Log,...這樣,我們冒險讓構造函數有很多參數,不是嗎?

是。你說你期望在構造函數中有15-20個參數:這根本不好。

您通常應該嘗試使用最多2-3個參數。不得不說,你的班級負有太多的責任,這對許多事情都有好處。

您可以嘗試將班級的代碼分成幾個更小/更有針對性的班級,或者使用事件來舉例。

如果我們把你的榜樣,您的打印機可能會比較像:

public function print($file) { 
    $this->driver->print($file); 

    $this->log->log('File has been printed'); 
    $this->monitor->sendCount(1); 

    $this->statsUpdater->increment(); 
} 

它更有意義:一臺打印機打印文件。

少一個依賴關係(隊列)。然後你可以有一個監視隊列的PrintQueueProcessor,拿下一個文件並調用打印機來打印它。

打印機執行一項作業(打印文件)時,隊列處理器執行一項作業(排隊文件以打印它們)。

相關問題