6

我喜歡用於依賴注入的構造函數注入。它強制從一種類型明確聲明關注,並有助於可測性。構造注入替代方案(Castle Windsor)

我喜歡構造函數注入,在地方...

記錄,那是我不喜歡它的一個例子。如果我有許多其他類繼承的基類,並且我希望所有這些類都使用我的ILogger(或其他)的實例,並且我不想要一個靜態工廠(Logger.Instance)... I不想在每個需要ILogger的子類上聲明一個構造函數。

所以,我可以有我的基類申報記錄作爲財產並將它注入這樣

public class MyBaseClass 
{ 
    public ILogger Logger { get; set; } 
} 

...但

  1. 這並不向我保證記錄儀實際上被注入並且不爲空。
  2. 我不喜歡與公共集

所以......我有什麼其他選擇ILogger? (我正在使用溫莎城堡)。

我考慮做一個接口

public interface IInitializable<T> 
{ 
    void Initialize(T instance); 
} 

public class MyBaseClass : IInitializable<ILogger>, ...could have other IInitializables too... 
{ 
    protected ILogger Logger { get; private set; } 

    public void Initialize(ILogger instance) 
    { 
     Logger = instance; 
    } 
} 

然後有我的容器上的設施,自動呼籲建築類型IInitializable<T>所有實現...

但我不知道還有什麼其他人民'的想法是在我走那條路線之前......

+0

爲什麼你不想使用靜態工廠模式? – 2010-12-09 19:03:48

+0

Ctor注入等,對於不同的測試等實現是非常有用的,但是你是否真的需要這種測試呢?您是否真的需要能夠在實例級別更改日誌記錄實施?靜態工廠模式仍然會使您能夠改變通常所需的* overall * logging實現。 – 2010-12-09 19:10:41

+0

記錄器返回的實現需要是動態的。具體來說Logger.Instance應該根據上下文而不同(即WCF操作,WPF客戶端,SL客戶端等)。我想我可以在.Current內部做一個服務定位器,但是這是一種反模式,據我所知... – Jeff 2010-12-09 19:42:35

回答

3

你這個過於複雜。 recommended and documented pattern to inject ILogger將具有NullLogger.Instance作爲默認值(即null object pattern)並使Logger成爲可選依賴項。爲記錄儀設置公共安裝員沒有任何問題。使用像您展示的那樣的自定義IInitializable可能只會使事情複雜化,而不會貢獻任何實際價值。

我將樣品從文檔複製在這裏,以供參考:

using Castle.Core.Logging; 

public class CustomerService 
{ 
    private ILogger logger = NullLogger.Instance; 

    public CustomerService() 
    { 
    } 

    public ILogger Logger 
    { 
     get { return logger; } 
     set { logger = value; } 
    } 

    // ... 
} 

編輯:看來這個問題其實是關於有根據上下文(其中有一點做以不同的記錄器的實現原始問題)。如果是這種情況,請使用服務覆蓋或處理程序選擇器。

0

如果您的基類本身不取決於​​,您應該從基類中移除該屬性。這樣你就可以保持基類構造器的清潔。

否則,您可以創建一個能夠創建MyBaseClass後代的工廠。這可能是這樣的:

public interface IMyBaseClassFactory 
{ 
    MyBaseClass CreateNew<TMyBaseClass>() where TMyBaseClass : MyBaseClass; 
} 

現在你可以創建一個註冊的IMyBaseClassFactory的實現,它能夠創建新實例並註冊的可選依賴關係:

public MyBaseClassFactoryImpl : IMyBaseClassFactory 
{ 
    public MyBaseClass CreateNew<TMyBaseClass>() 
    { 
     // Call your IoC container to get a descendant. 
     var instance = ServiceLocator.GetInstance<TMyBaseClass>(); 

     // Register missing dependencies 
     instance.Logger = ServiceLocator.GetInstance<ILogger>(); 

     return instance; 
    } 
} 

大多數IoC容器允許你來裝點注射屬性的屬性。然而,使用工廠的優勢在於,您的應用程序將完全不瞭解所使用的IoC框架。作爲缺點,還有更多的工作要做(創建工廠,注入工廠而不是實例本身,以及調用工廠來創建新實例)。

定義注入屬性時,需要使其可讀寫。當然,你不希望任何人在事後不小心重置該財產。如果沒有構造函數注入,編譯時支持很難實現這一點。但是,運行時檢查很容易:

private ILogger logger; 

public ILogger Logger 
{ 
    get { return this.logger; } 
    set 
    { 
     if (this.logger != null) 
     { 
      throw new InvalidOperationException("Logger has already been set."); 
     } 

     this.logger = value; 
    } 
} 

我希望這會有所幫助。