【问题标题】:Delegate vs interface with a single method for DI使用单一方法进行 DI 委托与接口
【发布时间】:2014-02-14 15:47:18
【问题描述】:

我有一个任务要做一些我想要限制的后台工作。我想注入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&lt;&gt;Func&lt;&gt;

目前,我计划创建一个单独的 static 类,其中包含上述各种 Throttle 实现。

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

这是正确的选择吗?我应该改用界面吗?

如果您认为答案主要基于意见,只需投票结束此问题,这也会有所帮助。

【问题讨论】:

  • 所以你只想知道依赖注入的最佳选择是什么:委托与接口
  • 注入了什么?这个依赖的目的是什么?知道这会给你答案。
  • 我已经完善了这个问题,并发布了有关我的具体案例的更多详细信息。

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


【解决方案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

【讨论】:

    【解决方案2】:

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

    在您的情况下,如果我不确定一个委托是我所需要的全部,我会通过使用接口使其可扩展。

    【讨论】:

      【解决方案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*/});
       }));
      

      【讨论】:

      • 大量的接口:我发现 DI 框架对于大多数应用程序来说过于矫枉过正,而且通常被过度使用的众多原因之一。
      • @Warrennenslin,我没有使用任何 DI 框架 - 更新了问题。还是谢谢。
      • 接口不仅使 DI 框架更容易,而且使维护应用程序更容易,因为接口比委托更不模糊。以 Func 为例。它返回什么?它可以返回一个 not 的东西,比如当前时间、当前日期,或者......我们不知道。另一方面,ITimeProvider 接口清楚地描述了对该依赖项的期望
      • @Steven,我可以强烈键入我的代表:public delegate DateTime ProvideTimeFunc()。然后,依赖类的构造函数:TimingUnit(ProvideTimeFunc provideTime)。真的和ITimeProvider差太多了吗?
      • @noseratio,那会好很多。这将解决歧义问题。
      猜你喜欢
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 2015-01-30
      • 2017-12-27
      • 1970-01-01
      • 2016-09-13
      • 2012-01-31
      相关资源
      最近更新 更多