使用单个DI方法委派vs接口

时间:2014-01-23 21:06:28

标签: c# .net interface dependency-injection delegates

我有一项任务正在做一些我想要扼杀的后台工作。我想注入void Throttle(taskState)方法。出于调试目的,它可以像Thread.Sleep(delay)一样简单,但它可能更复杂,可以进行一些日志记录等。

我在委托和具有单个方法的接口之间进行选择,作为任务驱动类的构造函数的参数。 选择哪个选项?

IMO,就DI而言,接口相对于代理的主要优势是可扩展性。可以轻松添加新方法。我可以创建interface I2: I1 { ... },拥有类实现I2,并仍然将其实例注入I1。客户端代码可以选择将其强制转换为I2,以查看是否支持新功能。

但是,如果我只需要注入一个方法,我认为无论是否需要维护状态,委托都会更有意义。代表们也可以保持状态,例如:

static Action<TaskState> GetThrottle(int delay) 
{ 
    return (s) => Thread.Sleep(delay++);
} 

我明确键入了我的委托,而不是使用Action<>Func<>

目前,我计划单独使用static类,其中包含上述各种Throttle个实现。

我没有在这个项目中使用任何DI框架。

这是正确的选择吗?我应该选择接口吗?

如果您认为答案主要是基于意见的,那么只需投票即可结束这个问题,这也有帮助。

3 个答案:

答案 0 :(得分:3)

将接口传递给构造函数的一个好处是它使得依赖解析更容易在DI框架中声明。如果有一个类似的。

public class ClassA{
    public ClassA(IInterface interface){
    ...
    }
}

然后使用像Unity这样的DI框架我可以很容易地注册类型

 container.RegisterType<IInterface, ConcreteImplementation>();

与代表们的关系变得有点困难。你可能需要做类似的事情。

public class ClassA{
    public ClassA(Delegate delegateInstance){
    ...
    }
}

然后注册依赖更加困难。

 container.RegisterType<ClassA>(
     new InjectionFactory(a => {
         return new ClassA(()=>{/*delegate code*/});
 }));

答案 1 :(得分:2)

在他的一次演讲中,智者David Chappell曾经说过,如果你面临的设计问题有几种方法可以解决它,你必须在捷径或路径之间做出决定,但有点困难但是提供可扩展性,选择以后。如果需要可扩展性可能会派上用场。如果你是在晚上开车,那么只有你的前灯才能看到。随着你的前进,道路变得越来越清晰。虽然这可能不适用于所有可能的情况,但这个建议对我帮助很大。

在你的情况下,如果我不确定一个代理是我将要用的所有代理,我会通过使用接口来扩展它。

答案 2 :(得分:1)

向界面添加额外方法的能力是一把双刃剑。几个类依赖于接口。一个类需要一些额外的依赖,有人决定将它推入现有的接口(及其实现)。他们不应该,但他们这样做。现在,依赖项有一个额外的方法,其他类不需要,因此违反了接口隔离。另外,依赖它的类正在做更多,但它更难说,因为它的依赖数没有增加。

您可以使用DI容器注册代表。一开始并不漂亮,但有一些扩展方法,没关系。可能存在静态方法适合的情况,并且注册静态方法作为委托的实现非常简单,因为没有类型可以注册或解析,只有委托本身。

以下是使用Autofac的示例:

代表:

public delegate Single DoMath(Single value1, Single value2);

扩展:

public static class AutofacBuilderExtensions 
{ 
    public static IRegistrationBuilder<TDelegate, SimpleActivatorData, SingleRegistrationStyle> RegisterDelegate<TDelegate, TSource>( 
        this ContainerBuilder builder,  
        Func<TSource, TDelegate> extractDelegate,  
        string sourceComponentName = null,  
        string registeredComponentName = null)  
        where TDelegate : class
    {
        var registrationFunction = new Func<IComponentContext, TDelegate>(context => 
        { 
            var c = context.Resolve<IComponentContext>(); 
            var source = sourceComponentName == null 
                ? c.Resolve<TSource>() 
                : c.ResolveNamed<TSource>(sourceComponentName); 
            return extractDelegate(source); 
        }); 

        return registeredComponentName == null ? 
            builder.Register(registrationFunction) : 
            builder.Register(registrationFunction) 
                .Named<TDelegate>(registeredComponentName); 
    } 
}

// register the type containing the method so it can be resolved,
// and then register the implementation of the delegate using the extension.
builder.RegisterType<AddsNumbers>();
builder.RegisterDelegate<DoMath, AddsNumbers>(addsNumbers => addsNumbers.DoMath);

或者,如果您只是注册静态实现:

builder.Register<DoMath>(context => MyStaticMathClass.AddsNumbers);

More details, plus Windsor extensions