委托(Lambda表达式)Vs接口和抽象类

时间:2012-06-11 16:11:31

标签: c# api design-patterns

我一直在寻找这个设计问题的简洁答案但没有成功。我无法在".NET Framework design guidelines""C# programing guidelines"找到帮助。 我基本上必须将模式公开为API,以便用户可以将他们的算法定义并集成到我的框架中,如下所示:

1)

// This what I provide
public abstract class AbstractDoSomething{
   public abstract SomeThing DoSomething();
}

用户需要实现这个抽象类,他们必须实现DoSomething方法(我可以从我的框架中调用并使用它)

2)

我发现这也可以通过使用代表来实现:

public sealed class DoSomething{
   public String Id;
   Func<SomeThing> DoSomething;
}

在这种情况下,用户只能以这种方式使用DoSomething类:

DoSomething do = new DoSomething()
{
  Id="ThisIsMyID",
  DoSomething = (() => new Something())
}

问题

这两个选项中的哪一个最适合简单,可用且最重要的可理解作为API公开?

修改

如果 1 :注册以这种方式完成(假设MyDoSomething延伸AbstractDoSomething

MyFramework.AddDoSomething("DoSomethingIdentifier", new MyDoSomething());

如果 2 :注册完成如下:

MyFramework.AddDoSomething(new DoSomething());

5 个答案:

答案 0 :(得分:15)

  

这两个选项中的哪一个最适合作为API公开的简单,可用且最重要的理解?

就OOP而言,第一个更“传统”,对许多开发人员来说可能更容易理解。它还可以在允许用户管理对象的生命周期方面具有优势(即:您可以让类实现IDisposable并在关闭时处理实例等),以及将来很容易扩展版本以不会破坏向后兼容性的方式,因为将虚拟成员添加到基类不会破坏API。最后,如果你想使用像MEF这样的东西自动组合它可以更简单,这可以从用户的角度简化/删除“注册”过程(因为他们可以创建子类,并将其放入文件夹,让它自动发现/使用。)

第二种是更具功能性的方法,并且在许多方面更简单。这允许用户使用现有代码的更改实现您的API,因为他们只需要在带有闭包的lambda中包装必要的调用,而不是创建新类型。

话虽如此,如果您打算使用委托的方法,我甚至不会让用户创建一个类 - 只需使用如下方法:

MyFramework.AddOperation("ThisIsMyID", () => DoFoo());

在我看来,这使得它更加清晰,您可以直接向系统添加操作。它还完全消除了对公共API(DoSomething)中另一种类型的需求,这再次简化了API。

答案 1 :(得分:6)

我会使用抽象类/接口if:

  • DoSomething是必需的
  • DoSomething通常会变得非常大(所以DoSomething的实现可以分成几个私有/受保护的方法)。

如果符合以下情况,我会与代表们一起去。

  • DoSomething可被视为事件(OnDoingSomething)
  • DoSomething是可选的(因此您将其默认为无操作委托)

答案 2 :(得分:2)

虽然我个人认为,如果在我手中,我总是会通过代表模型。我只是喜欢高阶函数的简洁和优雅。但在实现模型时,请注意内存泄漏。订阅事件是.Net中内存泄漏的最常见原因之一。这意味着,假设您有一个暴露了某些事件的对象,那么在所有事件都被取消订阅之前,原始对象永远不会被释放,因为事件会创建一个强引用。

答案 3 :(得分:1)

对于大多数这类问题而言,我会说“这取决于”。 :)

但我认为使用抽象类与lambda的原因实际上归结为行为。通常,我认为lambda被用作回调类型的功能 - 你喜欢在其他事情发生时自定义发生的事情。我在客户端代码中做了很多这样的事情:   - 拨打服务电话   - 获取一些数据   - 现在调用我的回调来相应地处理该数据

你可以对lambdas做同样的事情 - 它们是特定的,针对特定情况而定。

使用抽象类(或接口)实际上归结为您的类的行为由其周围的环境驱动。发生了什么,我与谁打交道等等?这些较大的问题可能表明您应该定义一组行为,然后允许您的开发人员(或您的API的使用者)根据他们的要求创建自己的行为集。当然,你可以对lambdas做同样的事情,但我认为开发起来会更复杂,而且与你的用户清楚沟通也会更复杂。

所以,我想我粗略的经验法则是: - 使用lambdas进行特定的回调或副作用定制行为; - 使用抽象类或接口来提供对象行为自定义的机制(或至少是对象主要行为的大部分)。

抱歉,我不能给你一个明确的定义,但我希望这会有所帮助。祝你好运!

答案 4 :(得分:1)

需要考虑的一些事项:

需要覆盖多少个不同的职能/代表?如果可以起作用,则无形的将以更容易理解的方式对覆盖的“集合”进行分组。如果您有一个“注册”功能,但许多子部分可以委托给实现者,这是“模板”模式的经典案例,这使得最有意义的是继承。

需要多少个不同的相同功能实现?如果只有一个,那么内在性很好,但如果你有很多实现,委托可能会节省开销。

如果有多个实现,程序是否需要在它们之间切换?或者它只使用一个实现。如果需要切换,代表可能会更容易,但我会提醒,特别是取决于#1的答案。参见战略模式。

如果覆盖需要访问任何受保护的成员,那么无效。如果它只能依赖公众,那么委托。

其他选择也可以是事件和扩展方法。