为什么类现在往往被定义为接口?

去年这2-3个,我看到的许多项目,如Cuyahoga开源C#CMS,倾向于将持久性和非持久性类定义为Interface 。 为什么? 有充分的理由吗? TDD? 惩戒? 一个设计模式? …

主要原因是这使得dependency injection等技术更容易。 这反过来又允许软件具有更大的灵活性,并且更容易重用和重新组合现有代码。 这有用的示例包括各种forms的unit testing(如您所述),以及大多数其他forms的“常规”代码重用。

一个简单的例子:

假设你有一个计算emplyoee工资的方法。 作为其签名的一部分,它接受一个计算其好处的对象,例如BenefitCalculator的一个实例:

 calculateSalary(... BenefitCalculator bc, ...) 

最初,您的设计只有一个类BenefitCalculator。 但后来,事实certificate你需要不止一个类,例如因为软件的不同部分应该使用不同的算法(可能支持不同的国家,或者因为算法应该是用户可配置的……)。 在这种情况下,不是膨胀现有的BenefitCalculator实现,而是创建新的类是有意义的,例如BenefitCalculatorFrance,或BenefitCalculatorSimple等。

现在,如果您使用签名

 calculateSalary(... BenefitCalculator bc, ...) 

,你有点搞砸了,因为你无法提供不同的实现。 但是,如果你使用

calculateSalary(… IBenefitCalculator bc,…)

你可以让所有类实现接口。

这实际上只是“松散耦合”的一个特例:需求尽可能少来自代码的其他部分。 在这种情况下,不要求某一类; 而只是要求存在某些方法,这正是接口的作用。

首先,您不能将类定义为接口。 您的类实现了一个接口。

接口用作启用多态行为的一种方法。 实现该接口的每个类都可以自由地指定它自己在接口中定义的方法的实现。 以下面的例子为例:

您正在编写银行软件。 你的任务是编写一个事务处理器。 现在,您知道您需要处理不同类型的交易(存款,取款,转账)。 您可以编写如下代码:

 public class TransactionProcessor { public void ProcessDeposit( // Process Deposit ); public void ProcessWithdraw( // Process Withdraw ); public void ProcessTransfer( // Process Transfer ); } 

然后,每当有人添加新的交易类型时,您必须修改您的类。 或者,您可以:

 public interface ITransaction { void Process(); } public class TransactionProcessor { public void ProccessTransaction(ITransaction t) { t.Process(); } } 

现在您不需要修改代码来处理新类型的事务。 您只需要人们创建自己的实现ITransaction的类,您的类将“只处理它”。

这允许您根据需要交换接口的实现。 它还支持dependency injection和模拟框架以进行unit testing。

但总的来说,它实际上只是让代码更灵活的另一种方法。

接口的优点是它们使您独立于实现,这是一件好事。

在过去几年中,IoC容器逐渐受到开发人员的欢迎。 例如,Microsoft Practices的Unity Container。 因此,在应用程序开始时,您可以注册实现接口的具体类,然后,例如,当通过实例化对象实例化时,将填充在其构造函数中包含这些接口的所有类,或者标记有[Dependency]属性的属性。 Unity容器的决心。 它在具有复杂依赖性的应用程序中非常有用,当一个界面可以在三个不同的类别中实现时。 如果不使用接口,所有这些都无法实现。

在一个非常无聊的级别接口也可以帮助加快编译速度。

 public class A { B b; } public class B { public int getCount() { return 10; } } 

在这种情况下,每次对B进行内部更改时,编译器都需要重新评估A以确定是否需要重新编译。

相反,我们使用接口:

 class A { IB b; } interface IB { int getCount(); } class B : IB { public int getCount() { return 10; } } 

在这种情况下, A仅取决于IB 。 对B的更改不需要在编译时考虑A。

在规模上,这种对短路依赖性评估的影响可以显着加速大型代码库的编译。 当有很多类依赖于单个类会发生很大变化时,它会特别强大。

显然,只有当类对实现类没有静态依赖时,此编译时权益才有效。 执行以下操作将完全失败这一好处:

 class A { IB b = new B(); } 

这就是dependency injection的来源.DI容器将构造一个B并将其作为IB提供给A ,因此A不需要具有静态依赖性。