簡體   English   中英

IoC、依賴注入和構造函數參數

[英]IoC, Dependency injection and constructor arguments

我有一個我希望能夠根據控制反轉原則創建的服務,所以我創建了一個接口和一個服務類。

public interface IMyService
{
    void DoSomeThing1();
    void DoSomeThing2();
    void DoSomeThing3();
    string GetSomething();

}

public class MyService : IMyService
{
    int _initialValue;
    //...

    public MyService(int initialValue)
    {
        _initialValue = initialValue;
    }

    public void DoSomeThing1()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing2()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing3()
    {
        //Do something with _initialValue
        //...
    }

    public string GetSomething()
    {
        //Get something with _initialValue
        //...
    }
}

例如使用 Unity 我可以設置我的 IoC。

public static class MyServiceIoc
{
    public static readonly IUnityContainer Container;

    static ServiceIoc()
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IMyService, MyService>();
        Container = container;
    }
}

問題是構造函數參數。 我可以使用 ParameterOverride 之類的

var service = MyServiceIoc.Container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

但我不想使用丟失類型的參數。 如果有人更改構造函數參數名稱或添加一個參數怎么辦? 他不會在完整的時間收到警告,也許除了最終用戶之外沒有人會發現它。 也許程序員為測試更改了他的 IoC 設置,但為了“發布”使用而忘記了它,那么即使是具有 100% 代碼覆蓋率的代碼庫也無法檢測到運行時錯誤。

可以向接口和服務添加 Init 函數,但是服務的用戶必須理解這一點,並記住每次獲取服務實例時調用 init 函數。 該服務變得不那么自我解釋,並且對不正確的使用開放。 如果方法不依賴於調用它們的順序,我最好。

讓它更安全的一種方法是在 Ioc 上有一個創建函數。

public static class MyServiceIoc
{
    //...
    public IMyService CreateService(int initialValue)
    {
        var service = Container.Resolve<IMyService>();
        service.Init(initialValue);

    }
}

但是如果你只看服務和它的接口,上面提到的問題仍然適用。

有沒有人對這個問題有一個強大的解決方案? 如何在仍然使用 IoC 的情況下以安全的方式將初始值傳遞給我的服務?

DI 容器是基於反射的,並且基本上是弱類型的。 這個問題比原始依賴要廣泛得多——它無處不在。

一旦您執行以下操作,您就已經失去了編譯時安全性:

IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
var service = container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

問題是您可以刪除第二個語句,並且代碼仍然可以編譯,但現在它將不再起作用:

IUnityContainer container = new UnityContainer();
var service = container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

請注意,編譯時安全性的缺乏與具體依賴關系無關,而是與涉及 DI 容器的事實有關。

這也不是 Unity 的問題; 它適用於所有 DI 容器。

某些情況下,DI 容器可能有意義,但在大多數情況下,純 DI是一種更簡單、更安全的替代方案:

IMyService service = new MyService(42);

在這里,如果其他人在您看向別處時更改了 API,您將收到編譯器錯誤。 這很好: 編譯器錯誤比運行時錯誤給你更直接的反饋


順便說一句,當您傳入 Primitive Dependency 並無形地將其轉換為Concrete Dependency 時,會使客戶端更難以理解發生了什么。

我建議改為這樣設計:

public class MyService : IMyService
{
    AnotherClass _anotherObject;
    // ...

    public MyService(AnotherClass anotherObject)
    {
        _anotherObject = anotherObject;
    }

    // ...
}

使用 Pure DI 組合仍然很容易且類型安全:

IMyService service = new MyService(new AnotherClass(42));

如何在仍然使用 IoC 的情況下以安全的方式將初始值傳遞給我的服務?

您可以在使用IUnityContainer.RegisterInstance方法在 Unity 中注冊時顯式調用類型的構造函數:

container.RegisterInstance<IMyService>(new MyService(42));

這將為您提供您提到的編譯時安全性,但代價是它只會被實例化一次,並且會立即創建(而不是第一次請求時)。

您也許可以通過使用接受 LifetimeManager 類的方法重載之一來處理這個缺點。

這取決於您的用例,但在 IoC 容器世界中,它可能如下所示:

public class MyService : IMyService
{
    int _initialValue;
    // ...

    public MyService(IConfigurationService configurationService)
    {
        _initialValue = configurationService.GetInitialValueForMyService();
    }
    
    // ...
}

如果您的帶有構造函數參數的類在您的代碼之外(例如在第 3 方庫中),您可以使用適配器。

public class AdaptedMyService : MyService
{
    public AdaptedMyService(IConfigurationService configurationService)
        : base(configurationService.GetInitialValueForMyService())
    {
    }
}

然后像這樣在 IoC 容器中注冊適配的類:

container.Register<IMyService, AdaptedMyService>();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM