编程到接口。 如何确定需要的地方?

我知道对接口进行编程有助于松散耦合。 但是,是否有一个解释何时最有效的指南?

例如,我有一个简单的Web应用程序,它收集员工的数据,他们的培训计划,费用和计算他们一年的费用等。这是一个相当简单的应用程序,我当然可以使用一个界面,但想知道是否会有任何使用。 我将使用它为了使用它。

总是可以说,随着应用程序的复杂性增加而我传递对象,传递类型(接口)比实现更有意义。 那么,我应该等待应用程序变得复杂或立即使用它吗? 我想知道最好的做法可能会变成“这家伙过度做事”。

编程到接口的原因是将更高级别的类与低级类的更改隔离开来。 如果您根本不期望更低级别的类更改,那么在这种情况下编程到接口是合理的。 本文(PDF)详细阐述了这一概念,创造了依赖性 – 反演原则这一术语。

如果您对测试驱动开发感兴趣,那么“对接口进行编程”的需求确实很明显。 如果希望在依赖于其依赖性的情况下测试类,则需要传递接口而不是对象。

让我了解Interfaces的一个简单例子就是这样

class SimpleClass { public int A { get; set; } public int B { get; set; } public int C { get; set; } } List Calc(IEnumerable list) { foreach(SimpleClass item in list) { item.C = item.A * item.C: } return list.ToList(); } 

注意IEnumerable输入参数。 通过使用它,我可以传入任何实现IEnumerable的集合作为in参数。 List, SimpleClass[], Collection

IEnumerable接口保证我可以使用foreach循环,这样我就使我的函数更通用了,因为IEnumerable变化的可能性小于f.ex,所以我必须更改此代码的可能性更小。 列出更改。

如果应用程序很有可能变得更加复杂,那么更容易设置脚手架而不是更晚。 但是,如果应用程序不复杂且不太可能不会变得复杂,则ROI可能不存在。 你以后总是可以重构。

在一个简单的应用程序中编程到一个接口,你不打算交换实现可能看起来有点矫枉过正。

第二个你进入unit testing,你会非常高兴能够编程接口,因为它让你能够更容易地用测试双打模拟真实对象。

您必须将接口视为合同。 该合同定义了签署此合同的人员需要履行的一系列规则和操作,无论其如何完成。

我通常这么想 – 只有两件事将界面与实现分开:

  1. 一个对象可以从许多接口inheritance,但只能从一个基类inheritance;
  2. 接口不允许默认实现,而基类则允许。

现在考虑将从“结构”inheritance的对象。 对他们来说更重要的是什么? 他们是否会从方法的默认实现中受益最多,或者如果他们可以拥有其他基类会更好吗?

在大多数情况下,很清楚哪一个是更重要的因素。 如果你碰巧在中间的细线上……运气不好。 Microsoft 建议基于接口的基类。

如果您希望能够测试您的应用程序而不必使用(并且随后不必构建)完整的员工对象,您可以创建IEmployee接口,然后创建轻量级测试友好的模拟员工对象以进行测试。 如果创建一名员工很难甚至不可能手工或没有数据库,这可能是一个巨大的收获。

另一个很好的理由是它可以帮助您定义您在员工类中完全依赖的内容。 您可能认为您只使用了一些公共方法,但后来发现您使用了10种方法,并且它们比您想象的更紧密耦合。

最后,如果您需要更改代码以使用SuperEmployee而不是Employee,如果您一直使用接口,那么您需要做的就是让SuperEmployee实现IEmployee并且您将被设置。

浏览Head-First设计模式一书……你会看到使用与TDD或多态无关的接口的好参数。

接口允许您在运行时更改对象的行为…想一想您需要特定行为的处理程序的位置,但可能不知道在运行时需要什么行为。 在计算员工费用的情况下……管理人员可能比普通员工享有更高的“娱乐”费用。

如果您的Employee对象具有对IExpenseCalculator接口的引用,则可以在运行时为其分配管理器的计算器或员工的计算器。 调用Employee.GetExpenses()将为经理提供与常规员工不同的计算结果。 在内部,代码看起来像这样:

 public class Employee { private IExpenseCalculator expenses; public ExpenseSheet GetExpenses() { return expenses.CalcExpenses(); } } 

此示例假定’expenses’作为属性公开,并且IExpenseCalculator具有CalcExpenses()的方法。

接口也与对象工厂的概念密切相关……想想数据库层。 将数据层配置为工厂后,可以根据配置设置在运行时动态创建对象以连接到Sql Server,Oracle或MySql。 但是客户端需要数据库层对象的具体句柄…进入接口。

接口是一种强大的工具,经常被误用。 正确使用它们会改变您的思维方式,但可以极大地帮助您的应用程序结构。

对于属于API的任何内容,无论是外部客户还是重用代码的其他团队,都要尽可能多地使用接口。 这是值得的,因为它尽可能地隐藏了您的实现如何工作,并为您提供更大的自由,以便在将来增强它们。 只显示一个可从中获取实例的小型具体(可能是静态)类。

对于设计的内部部分,您可以选择从任何地方的具体类引用开始,并且只引入对设计有意义的接口。

但另一个需要考虑的重要事项是unit testing。 您可以在没有技术困难的情况下完全“模拟”CLR中的界面,但是嘲弄其他事情要么是不可能的,要么需要一些严厉的诡计。 因此,需要考虑的一个原则是测试驱动开发:随着代码编写代码的测试,您将发现需要某些东西由接口表达,因此您可以提供它们的模拟版本。