2009-03-02 36 views
5

希望我能解釋這個有點體面,因爲它今天在我的大腦中引發了一個引線。我在C#中學習TDD,所以我仍然試圖重新佈線我的大腦以適應它。TDD:靜態方法,依賴注入,緩存和你!

比方說,我有一個用戶類,該類以前有一個靜態方法來檢索用戶對象(下面簡化)。

public static User GetUser(string username) 
{ 
    User user = GetUserFromCache(username); 
    if(user == null) 
    { 
     user = GetUserFromDatabase(username); 
     StoreObjectInCache(user); 
    } 
    return user; 
} 

所以我想改寫這個使用依賴注入,所以我可以僞造出「GetUserFromDatabase」的方法,如果它需要去那裏。這意味着我必須使該功能不是靜態的。而數據訪問層將從數據庫構建用戶對象,將返回的列映射到對象屬性,而從緩存中檢索將返回真藍色的對象。然而,在一個非靜態的方法中,我不能只說

this = GetUserFromCache(username); 

因爲它只是不這樣工作。雖然我絕不是如何與OO一起跳舞的世界專家,但它看起來像我幾乎不得不從緩存中獲取User對象並編寫另一個映射函數,該函數會將返回的User對象屬性存儲到新的用戶實例。

這裏有什麼解決方案?我失蹤的任何OO魔法?是唯一的解決方案重構所有使用工廠,而不是在對象本身具有實例化邏輯?還是我一直盯着這個太久而錯過了一些完全明顯的東西?

回答

7

我不認爲你錯過了任何魔法,我認爲重構將持久代碼從業務對象和持久層中移除是從單元測試和設計角度來看的正確方法。您可能想考慮讓緩存位於業務層和持久層之間,調解業務對象的檢索/更新以簡化事情。如果以這種方式分離出來,你應該能夠嘲笑/僞造你的緩存和持久層。

+0

感謝您的反饋!至於將緩存邏輯移動到持久性,我很好奇,如果我實例化的對象有一堆對緩存對象的引用,然後緩存的對象退出緩存,會發生什麼情況。我頭疼。 – Chris 2009-03-02 20:32:05

+0

+1:絕對需要更多的關注問題。將緩存作爲應用程序和持久層之間的單獨層是非常有意義的。 – 2009-03-02 20:35:45

4

Unit testing code that does date processing based on today's date

之前

  • 有一個使用數據庫來獲取用戶,並將其放置到緩存中的一些代碼。

  • 後有一個使用數據庫來獲取用戶
  • 一些代碼有一些地方的用戶在高速緩存代碼。

這兩組代碼不應相互依賴。

public static Func<string, UserName> Loader {get;set;} 

public static Constructor() 
{ 
    Loader = GetFromDataBase; 
} 

public static User GetUser(string userName) 
{ 
    User user = GetUserFromCache() 
    if (user == null) 
    { 
    user = Loader(userName); 
    StoreUserInCache(user); 
    } 
    return user; 
}  

public void Test1() 
{ 
    UserGetter.Loader = Mock.GetUser; 
    UserGetter.GetUser("Bob"); 
} 

傳統上,將使用接口而不是Func。如果涉及多種方法,則界面是Func的明顯選擇。如果方法實現本身是靜態的,則Func是對其進行抽象的一種方式。

1

我在你的例子中缺少的是你調用「GetUser」的上下文。這可能是因爲使用靜態方法,您無需考慮,因爲您可以從任何地方調用它。在DI中,這意味着存儲庫需要由發件人以某種方式引用,這是最有可能的字段。

當你的緩存是某個對象的字段,可能是一個門面,你可以使用這個緩存代理你的數據庫。

所以你會:

class ApplicationFacade{ 
    private IUserRepository users = null; 

    public doStuff(){ 
    this.users.GetUser("my-name"); 
    } 
} 

其中IUserRepository是你的緩存,假數據庫和數據庫的通用接口。一些簡單的像:

interface IUserRepository{ 
    User GetUser(string username); 
} 

緩存現在可以實現此接口的簡單對象,因爲緩存注入DI容器也可以注入到它。

class Cache : IUserRepository { 
    private IUserRepository users = null; 
    public User GetUser(string username){ 
    if (this.NotCached(username)){ 
     this.ToCache(this.users.GetUser(username)); 
    } 
    return this.FromCache(username); 
    } 
} 

現在取決於你願意,你可以注入你的假,高速緩存或數據庫到你的外觀對象,如果您使用緩存對象您可以根據需要注入你的假呃數據庫到它(甚至是其他的緩存什麼如果你真的想)。

因爲實際的注入機制取決於你的DI容器,並可能需要一些額外的代碼作爲公共屬性或構造字段。