2010-11-03 37 views
2

我們的應用程序中有一個相當普遍的對象。在這種情況下,我們稱之爲球。球運行良好,但在一些配置中,它們的作用不同。它目前的設置是這樣的:具有配置依賴性的常用對象

class Ball 
{ 
    private static readonly bool BallsCanExplode; 
    static Ball() 
    { 
     bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"], 
      out BallsCanExplode); 
    } 
    public Ball(){} 
} 

這在實踐中完全正常。如果配置是球可以爆炸,它們會爆炸,如果沒有爆炸,則不爆炸。問題是它完全不可測試。我一直無法找出一個好方法來保持它的可測試性,並且仍然很容易實例化。

最簡單的解決方法就是解耦球和配置:

class Ball 
{ 
    private readonly bool CanExplode; 
    public Ball(bool canExplode); 
} 

這樣做的問題是,這個曾在Ball類孤立的依賴,現在已經蔓延到每一個班級,使一個球。如果這種依賴被注入,那麼爆炸球的知識必須注入到處。

BallFactory存在同樣的問題。雖然每個班級都可以去new Ball(),但現在必須知道必須在任何地方注入的BallFactory。另一種選擇是使用已經被烤到應用程序的服務定位器:

class Ball 
{ 
    private readonly bool CanExplode; 
    public Ball() 
    { 
     CanExplode = ServiceLocator.Get<IConfiguration>().Get("ballsCanExplode"); 
    } 
} 

這仍保持在球的配置依賴,但允許測試配置在被注入球用這麼多。儘管如此,在每個new Ball()調用中找到該服務似乎有點矯枉過正。

保持這種可測試性以及易於實例化的最佳方法是什麼?

注意:應用程序中同時存在依賴注入框架和服務定位器,這兩者都經常使用。

+0

那些D.I.像Ninject這樣的庫有幫助嗎? – xandy 2010-11-03 15:35:08

+0

您似乎意識到依賴注入選項,但不想遵循它們。因此,你最好的選擇是添加一個構造函數進行測試:public Ball(bool CanExplode) – 2010-11-03 21:30:19

回答

5

實例化球的類應接收BallFactory作爲依賴項。 BallFactory可以相應地配置爲應用程序啓動,無論是否產生爆炸球或非爆炸球。

沒有BallFactory讀取應用程序配置文件來確定生成哪種類型的球。應該注入BallFactory

服務定位器是一種反模式。不要使用它們。

+0

如果你沒有其他的依賴注入方法,服務定位器可以成爲一種乾淨的方式來實現這一點。在許多情況下,工廠可能會被矯枉過正,但在這種特殊情況下(您需要創建多個相同類的實例並解決每種情況下的設置)可能適用。 – 2010-11-03 15:37:50

+0

服務定位器已在應用程序中隨處可見,所以它已經存在了,無論它是否是反模式。有些東西使用它,有些東西使用依賴注入。 – Snea 2010-11-03 15:38:32

+2

@Snea:嗯,我認爲你應該停止在服務定位器上添加更多的依賴關係。 – jason 2010-11-03 15:54:09

0

我投票支持配置服務路徑。典型的服務定位器實現的開銷不應該太高,如果以後需要,可以緩存配置服務。更好的是,使用依賴注入框架,你不需要明確定位服務。

0

怎麼是這樣的:

internal interface IBallConfigurer 
{ 
    bool CanExplode { get; } 
} 

internal class BallConfigurer : IBallConfigurer 
{ 
    public bool CanExplode 
    { 
     get 
     { 
      bool BallsCanExplode; 
      bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"], 
     out BallsCanExplode); 
      return BallsCanExplode; 

     } 
    } 
} 

public class Ball 
{ 
    private bool canExplode; 

    public Ball() 
     :this(new BallConfigurer()) 
    { 

    } 

    internal Ball(IBallConfigurer ballConfigurer) 
    { 
     this.canExplode = ballConfigurer.CanExplode; 
    } 
} 

這樣一來,就可以使球類的內部可見的單元測試組裝,並注入定製ballconfigurer。

+0

這實質上是我嘗試的第一件事,但是使用烘焙的BallConfigurer似乎打破了將球從AppSettings類中解耦的目的。 – Snea 2010-11-03 15:42:39

+0

那麼,你說你想讓球容易實例化,但你想要它可測試。這給你所有的。你從來沒有說任何關於球與AppSetting類緊密結合的東西,你只是希望它足夠脫離單元測試。這給你。 – BFree 2010-11-03 15:46:08

2

我會用東西喜歡你的服務定位器設置靜態 DefaultBallsCanExplode,那麼也許有一個重載的構造函數,可以採取一個ballsCanExplode布爾作爲一個選項。

保持簡單!

2

據我所知,你的應用程序中的所有球總是相同的。它們或者爆炸或者它們不是,由配置開關確定。你可以做的是在你的DI框架中進行配置。根據架構在應用程序根接線可能看起來像這樣::

bool ballsCanExplode = 
    bool.Parse(ConfigurationManager.AppSettings["ballsCanExplode"]); 

container.Register<Ball>(() => new Ball(ballsCanExplode)); 

當你這樣做,你可以使用Service Locator模式來獲取一球的新實例,因爲你已經習慣做的事:

ServiceLocator.Get<Ball>(); 

但更好的方式是讓DI框架在一些其他類型的(多用於測試更容易)的構造函數注入Ball依賴。

+1

+1配置是應用程序初始化的一部分,而不是其運行時。 – 2010-11-03 23:17:39

相關問題