依赖注入和初始化方法

ker*_*nix 2 c# unit-testing design-patterns dependency-injection simple-injector

我阅读了Miško Hevery 的指南:编写可测试代码,如果“构造函数完成后对象未完全初始化(注意初始化方法)”,它会指出一个警告标志。

假设我写了一个 Redis 包装类,它有一个接受主机名和端口的 init 方法。根据 Miško 的说法,这是一个警告信号,因为我需要调用它的 init 方法。

我正在考虑的解决方案如下:对于每个需要这种初始化的类,创建一个工厂类,该类具有创建类的 Create 方法,并调用其 init 方法。

现在在代码中:而不是使用类似的东西:

class Foo
{
    private IRedisWrapper _redis;
    public Foo(IRedisWrapper redis)
    {
       _redis = redis;
    }
}
....
IRedisWrapper redis = new RedisWrapper();
redis.init("localhost", 1234);
Foo foo = new Foo(redis);
Run Code Online (Sandbox Code Playgroud)

我会使用类似的东西:

class Foo
{
    private IRedisWrapper _redis;
    public Foo(IRedisWrapper redis)
    {
       _redis = redis;
    }
}
....
RedisWrapperFactory redisFactory = new RedisWrapperFactory();
IRedisWrapper redisWrapper = redisFactory.Create();
Foo foo = new Foo(redisWrapper);
Run Code Online (Sandbox Code Playgroud)

我正在使用Simple InjectorIOC 框架,这使得上述解决方案成为问题 - 在这种情况下,我会使用类似的东西:

class Foo
{
    private RedisWrapper _redis;
    public Foo(IRedisWrapperFactory redisFactory)
    {
       _redis = redisFactory.Create();
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的很想听听您对上述解决方案的意见。

谢谢

Ste*_*ven 5

也许我误解了您的问题,但我不认为 Simple Injector 是这里的限制因素。由于构造函数应尽可能少做,因此不应Create从构造函数内部调用该方法。这甚至是一件奇怪的事情,因为工厂旨在延迟类型的创建,但由于您Create在构造函数内部调用,因此不会延迟创建。

您的Foo构造函数应该简单地依赖IRedisWrapper并且您应该redisFactory.Create()像这样提取对DI 配置的调用:

var redisFactory = new RedisWrapperFactory();

container.Register<IRedisWrapper>(() => redisFactory.Create());
Run Code Online (Sandbox Code Playgroud)

但是由于工厂的唯一目的是防止整个应用程序中的重复初始化逻辑,它现在失去了它的目的,因为工厂被使用的唯一地方是在 DI 配置中。所以你可以把工厂扔掉,写下下面的注册:

container.Register<IRedisWrapper>(() =>
{
    IRedisWrapper redis = new RedisWrapper();
    redis.init("localhost", 1234);
    return redis;
});
Run Code Online (Sandbox Code Playgroud)

您现在将Create方法的主体放置在匿名委托中。你的RedisWrapper类目前有一个默认的构造函数,所以这种方法很好。但是如果它RedisWrapper开始获得自己的依赖项,最好让容器创建该实例。这可以按如下方式完成:

container.Register<IRedisWrapper>(() =>
{
    var redis = container.GetInstance<RedisWrapper>();
    redis.init("localhost", 1234);
    return redis;
});
Run Code Online (Sandbox Code Playgroud)

当您需要在创建后初始化您的类时,RedisWrapper显然需要,建议的方法是使用该RegisterInitializer方法。最后的代码片段可以写成如下:

container.Register<IRedisWrapper, RedisWrapper>();

container.RegisterInitializer<RedisWrapper>(redis =>
{
    redis.init("localhost", 1234);
});
Run Code Online (Sandbox Code Playgroud)

这会注册在请求RedisWrapperan 时返回的 ,IRedisWrapperRedisWrapper使用注册的初始化程序进行初始化。这种注册可以防止对容器的隐藏回调。这提高了性能并提高了容器诊断配置的能力。