2010-07-13 93 views
0

我正在更新絕對充滿Singleton類的遺留應用程序。一個很好的例子將是SnmpConnector類:在不使用Singleton的情況下封裝昂貴的資源

public SnmpConnector 
{ 
    public static IEnumerable<string> HostIpAddresses 
    { 
    ... 
    } 

    private static SnmpConnector instance; 
    public static SnmpConnector Instance 
    { 
    if (instance == null) 
     instance = new SnmpConnector(); 
    return instance; 
    } 

    private SnmpConnector() 
    { 
    foreach (string IpAddress in HostIpAddresses) 
    { 
     ... 
    } 
    } 

    ... 
} 

此更新的目的是爲了提高代碼庫的可測性,因此我想擺脫單身的。我已經抽象化了的SnmpConnector的數據源從測試數據庫或查詢活動服務器要麼得到的數據:

public interface ISnmpDataSource 
{ 
    public DataTable MacTable 
    { 
    get; 
    private set; 
    } 

    public DataTable PortTable 
    { 
    get; 
    private set; 
    } 

    ... 
} 

public TestSnmpDataSource : ISnmpDataSource 
{ 
    public FileInfo DataSource 
    { 
    get; 
    private set; 
    } 

    ... 
} 

public SnmpDataSource : ISnmpDataSource 
{ 
    public List<string> HostIpAddresses 
    { 
    get; 
    private set; 
    } 

    ... 
} 

public SnmpConnector 
{ 
    public SnmpConnector(ISnmpDataSource DataSource) 
    { 
    ... 
    } 

    ... 
} 

現在,我想測試這些組件,並運行到問題可能會導致SnmpConnector成爲單身人士:它需要花費不合時宜的時間來測試SnmpDataSource。事實證明,從實時交換機獲取MAC表和端口表需要10到20秒之間的時間。我已經爲這個特定的類寫了13個單元測試,所以完成這些測試需要兩分多鐘。就像這樣令人討厭,一旦這些更新發布到我們原來的代碼庫,它會變得更糟。有了這個新的重構,沒有什麼能阻止程序員反覆創建和丟棄一個SnmpDataSource。

現在,這些表格中的數據基本上是靜態的;舊的Singleton和新的SnmpDataSource都維護一個只能每四小時更新一次的緩存。我需要使SnmpDataSource成爲一個Singleton來防止這個問題嗎?

回答

3

使用依賴注入,並將SnmpDataSource傳遞給需要它的任何東西,或者可能傳遞Func<SnmpDataSource>,這可以根據需要延遲創建實例。

您的目標是SnmpDataSource應該自行更新,或者呼叫者在幾小時後會獲得新版本?

+0

SnmpDataSource只是知道如何通過與交換機通信返回幾個表。 SnmpConnector然後緩存這些數據並繼續其快樂的方式。但是,SnmpDataSource需要10-20秒才能獲取數據。我已經將SnmpDataSource注入到SnmpConnector中,但可以爲SnmpConnector的兩個不同實例創建兩個不同的SnmpDataSource,從而導致20-40秒的延遲。 – DonaldRay 2010-07-13 20:22:49

+0

應該補充一點,我的目標是沒有任何SnmpDataSource的實例出去獲取數據,如果有任何實例已經在過去4個小時內完成了這些。 – DonaldRay 2010-07-13 20:29:57

+0

@DonaldRay:你可以傳入一個'Func '緩存......它基本上像單例一樣,但是以可注入(可測試)的方式。 – 2010-07-13 20:41:32

0

您可以嘗試使用實現相同接口的緩存感知版本來打包/裝飾SnmpDataSource,然後插入緩存感知版本。編輯 - 或者你可以做Jon建議工廠Func在那裏做緩存的地方(取決於最後一個被創建的時間,它會返回一個新的實例或一個緩存的版本)。同樣的事情,稍微不同的實現。 Jon的版本可能更有意義。

public CachedSnmpDataSource : ISnmpDataSource 
{ 
    private DateTime m_lastRetrieved; 
    private TimeSpan m_cacheExpiryPeriod; 
    private List<string> m_hostIpAddresses; 
    private Func<SnmpDataSource> m_dataSourceCreator 

    public CachedSnmpDataSource(Func<SnmpDataSource> dataSourceCreator, TimeSpan cacheExpiryPeriod) 
    { 
     m_dataSourceCreator = dataSourceCreator; 
     m_cacheExpiryPeriod = cacheExpiryPeriod; 
    } 

    public List<string> HostIpAddresses 
    { 
    get 
    { 
     if(!IsRecentCachedVersionAvailable()) 
     { 
      CreateCachedVersion(); 
     } 

     return new List<string>(m_hostIpAddresses); 
    } 

    private bool IsRecentCachedVersionAvailable() 
    { 
     return m_hostIpAddresses != null && 
       (DateTime.Now - m_lastRetrieved) < m_cacheExpiryPeriod; 
    } 

    private void CreateCachedVersion() 
    { 
     SnmpDataSource dataSource = m_dataSourceCreator(); 
     m_hostIpAddresses = dataSource.HostIpAddresses; 
     m_lastRetrieved = DateTime.Now;  
    } 
    } 

    ... 
} 
0

經過幾次迭代後,我最終解決了這個問題。我將按照原樣離開接受的答案,但這是我最終使用的:

  1. ISnmpDataSource負責提取數據,和以前一樣。
  2. SnmpConnector知道只查詢自己的緩存是否無效。
  3. 靜態工廠類維護Dictionary<ISnmpDataSource, SnmpConnector>。根據這本詞典有一個static BuildSnmpConnector(ISnmpDataSource)方法。

現在使用的庫看起來是這樣的:

IEnumerable<string> IpAddresses = ...; 
string SqlConString = @"..."; 
ISnmpDataSource Switches = new SnmpDataSource(IpAddresses, SqlConStr); 
SnmpConnector instance = Factory. BuildSnmpConnector(Switches); 

我曾與我是如何實現的ISnmpDataSource實現GetHashCodeEquals,但正式的平等幾乎修復了所有這些問題的定義的幾個問題。

我很滿意最終結果; Factory類負責限制實例化,而SnmpConnector負責緩存查詢結果,而ISnmpDataSource負責實際運行查詢。我確信有一個更好的組織,但這個是足夠乾淨的使用。

相關問題