2012-05-07 55 views
2

很多時候我聽說避免靜態類因爲they will insert dependencies that will render your code unusable in other projects, and will not allow to unit test itPHP避免靜態類避免依賴關係,但我需要全局遍地使用

比方說,我們有一個典型的class DB訪問數據庫,如果這樣的類是static,我們可以把它叫做無論在我們的代碼:

DB::execQuery(...); 

但是這創造依賴關係,因此,讓DB類NOT靜態的,在這種情況下,我們有地方在我們的代碼:

$db = new DB(); 

,然後我們就可以在我們的代碼中調用

$db->execQuery(...); 

但現在每次使用$db一個function我們需要內部時,先聲明它這樣

global $db;

有沒有辦法來解決這個?

的一種方法是注入在使用它的類$db對象,但我會在所有使用它的類注入它,這是ridicolous,靜態類會更快與少的工作代碼寫入。我錯過了什麼?

+0

http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/ –

回答

5

$ db可以在實例化時注入到屬性中,那麼您只需要訪問此屬性而不是將它傳遞給每個方法。

class MyClass { 
    protected $_db; // DB Connection 
    public function __construct($db) { 
    $this->_db = $db; 
    } 

    public function foo() { 
    $this->_db->query('...'); 
    } 

} 

除此之外,你可以看看有服務容器(又名依賴注入容器),其改掉表現得像一個全局變量,但解決一些測試問題。看看其中的一些相關的問題

有一個DI容器,您可以使用靜態方法在你的類一樣DI_Container::get('db')。它看起來很像global或其他一些靜態調用..但在這種情況下DI_Container包含特殊的方法,允許在測試和其他情況下采取額外的行動..消除一些全球性的「惡性」。

+1

邪惡的全球。嗯。我也可以在測試中重寫我的全局變量。用一個替換許多靜態函數和全局變量可能會好一點,但我感覺這不是疾病的正確治癒。 – hakre

+0

@Mike B:好吧,如果我必須調用'ServiceContainer :: $ db-> execQuery(...)',我寧願繼續調用'DB :: execQuery(...)'它更短。 –

+0

@MarcoDemaio使用服務容器,您可以選擇在測試執行前用可測試版本替換'DB'的實例。沒有容器,你必須在'DB'類中有這個邏輯,這可以導致高迴圈複雜度分數。我並不是說DI是你的情況的最佳答案,只是開發者有時在這種情況下達到的一個工具。所有的框架都在某種程度上處理這個問題..一切都需要在所有代碼層訪問數據庫,配置和註冊表對象 - 在全局名稱空間中沒有處理對象的最終方法。 –

1

除了Mike B的回答,我會指出你的代碼中的錯誤設計是:「我們可以在代碼中的任何地方調用它」。

實際上,數據庫只能用於你的模型或應用程序中必須知道的數據庫的一小部分。因此這些類應該知道有一個數據庫,並將其用作依賴項(如Mike B所說,通過構造函數傳遞)。

但是其餘的應用程序不應該關心數據庫,它應該只關心模型。關注重構並將訪問數據庫的所有代碼收集到Model類中。

這樣,您的應用程序將具有一個具有依賴關係的Model層:數據庫對象/連接。而其餘的應用程序都將使用該模型,無論Controller/View業務中的模型如何。

享受重構。